Source

DuAEF_Duik_api.jsxinc

// ==================== |---------| ====================
// ==================== | api_all | ====================
// ==================== |---------| ====================

// ==================== |-------| ====================
// ==================== | DuAEF | ====================
// ==================== |-------| ====================

// ==================== |-------| ====================
// ==================== | DuESF | ====================
// ==================== |-------| ====================

// ==================== |------| ====================
// ==================== | core | ====================
// ==================== |------| ====================

/* License
	DuESF - Duduf ExtendScript Framework

	Copyright (c) 2021 Nicolas Dufresne, RxLaboratory

	https://rxlaboratory.org

	This file is part of DuESF.

        This program is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.

        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * The Duduf ExtendScript Framework.<br />
 * by {@link https://rxlaboratory.org RxLaboratory} and {@link http://duduf.com Duduf}.<br />
 * WARNING: all objects marked deprecated will be removed in the version 1.1.0 of the framework.
 * @example
 * // Encapsulate everything to avoid global variables!
 * // The parameter is either undefined (stand alone script) or the panel containing the ui (ScriptUI)
 * (function(thisObj)
 * {
 *      // Include the framework
 *      #include "DuESF.jsxinc";
 *      
 *      // Running the init() method of DuAEF is required to setup everything properly.
 *      DuESF.init( "YourScriptName", "1.0.0", "YourCompanyName" );
 *      
 *      // These info can be used by the framework to improve UX, but they're optional
 *      DuESF.chatURL = 'http://chat.rxlab.info'; // A link to a live-chat server like Discord or Slack...
 *      DuESF.bugReportURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a bug report form
 *      DuESF.featureRequestURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a feature request form
 *      DuESF.aboutURL = 'http://rxlaboratory.org/tools/dugr'; // A link to the webpage about your script
 *      DuESF.docURL = 'http://dugr.rxlab.guide'; // A link to the documentation of the script
 *      DuESF.scriptAbout = 'Duduf Groups: group After Effects layers!'; // A short string describing your script
 *      DuESF.companyURL = 'https://rxlaboratory.org'; // A link to your company's website
 *      DuESF.rxVersionURL = 'http://version.rxlab.io' // A link to an RxVersion server to check for updates
 *      
 *      // Build your UI here, declare your methods, etc.
 * 
 *      // This will be our main panel
 *      var ui = DuScriptUI.scriptPanel( thisObj, true, true, new File($.fileName) );
 *      ui.addCommonSettings(); // Automatically adds the language settings, location of the settings file, etc
 *
 *      DuScriptUI.staticText( ui.settingsGroup, "Hello world of settings!" ); // Adds a static text to the settings panel
 *      DuScriptUI.staticText( ui.mainGroup, "Hello worlds!" ); // Adds a static text to the main panel
 *      
 *      // When you're ready to display everything
 *      DuScriptUI.showUI(ui);
 *
 *      // Note that if you don't have a UI or if you don't use DuScriptUI to show it,
 *      // you HAVE TO run this method before running any other function:
 *      // DuESF.enterRunTime();
 *  
 * })(this);
 * @namespace
 * @author Nicolas Dufresne and contributors
 * @copyright 2021 Nicolas Dufresne, RxLaboratory
 * @version 1.0.0
 * @license GPL-3.0 <br />
 * This program is free software: you can redistribute it and/or modify<br />
 * it under the terms of the GNU General Public License as published by<br />
 * the Free Software Foundation, either version 3 of the License, or<br />
 * (at your option) any later version.<br />
 *<br />
 * This program is distributed in the hope that it will be useful,<br />
 * but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br />
 * GNU General Public License for more details.<br />
 *<br />
 * You should have received a copy of the GNU General Public License<br />
 * along with this program. If not, see {@link http://www.gnu.org/licenses/}.
 * @category DuESF
 */
var DuESF = {};

/**
 * The Current DuESF Version
 * @readonly
 * @memberof DuESF
 * @type {DuVersion}
 */
DuESF.version = new DuVersion('1.0.0');
/**
 * Set to true and enable debug mode if you're a developper
 * @memberof DuESF
 * @type {Boolean}
 */
DuESF.debug = false;
/**
 * The name of the script using this instance of DuESF. Must be set by {@link DuESF.init}.
 * @memberof DuESF
 * @type {string}
 * @readonly
 */
DuESF.scriptName = "DuESF";
/**
 * The version of the script using this instance of DuESF. Must be set by {@link  DuESF.init}.
 * @memberof DuESF
 * @type {DuVersion}
 * @readonly
 */
DuESF.scriptVersion = new DuVersion('0.0.0');
/**
 * An icon for this script
 * @type {string}
 */
DuESF.scriptIcon = '';
/**
 * The url for the forum about the script
 * @type {string}
 */
DuESF.forumURL = '';
/**
 * The url for a chat server about the script
 * @type {string}
 */
DuESF.chatURL = '';
/**
 * The url for reporting bugs
 * @type {string}
 */
DuESF.bugReportURL = '';
/**
 * The url for donations, support, like...
 * @type {string}
 */
DuESF.donateURL = 'http://donate.rxlab.info';
/**
 * The url about the script
 * @type {string}
 */
DuESF.aboutURL = '';
/**
 * The url to the documentation of the script
 * @type {string}
 */
DuESF.docURL = '';
/**
 * The text about the script
 * @type {string}
 */
DuESF.scriptAbout = '';
/**
 * The name of the company/organisation/individual developping this tool
 * @type {string}
 */
DuESF.companyName = 'RxLaboratory';
/**
 * The URL to the company's website
 * @type {string}
 */
DuESF.companyURL = 'https://rxlaboratory.org';
/**
 * An icon for this company
 * @type {string}
 */
DuESF.companyIcon = '';
/**
 * The URL to a rxVersion server used to check for updates
 * @type {string}
 */
DuESF.rxVersionURL = '';
/**
 * A URL where people can help localizing the script
 * @type {string}
 */
DuESF.translateURL = 'http://translate.rxlab.io'
/**
 * Is this script a prerelease version? This info is used when checking for updates.
 * @type {bool}
 */
DuESF.isPreRelease = false;
/**
 * The current DuESF File
 * @readonly
 * @memberof DuESF
 * @type {File}
 */
DuESF.file = new File( $.fileName );
/**
 * Enum for states.
 * @readonly
 * @enum {int}
 */
DuESF.State = {
    NOT_SET: -1,
    INIT: 0,
    RUNTIME: 1
};
/**
 * The current state of the script
 * @type {DuESF.State}
 * @default DuESF.State.NOT_SET
 */
DuESF.state = DuESF.State.NOT_SET;
/**
 * True if the script is run for the first time (new or the version has changed since last run)
 * @type {boolean}
 * @readonly
 */
DuESF.scriptFirstRun = true;
/**
 * The applicatiosns DuESF can be used with.
 * @readonly
 * @enum {string}
 */
DuESF.HostApplication = {
    AFTER_EFFECTS: 'aftereffects',
    ILLUSTRATOR: 'illustrator',
    PHOTOSHOP: 'photoshop',
    INDESIGN: 'indesign'
}
DuESF.hostIsBeta = false;
/**
 * The host application
 * @type {DuESF.HostApplication}
 */
DuESF.host = null;
/**
 * The version of the host application
 */
DuESF.hostVersion = new DuVersion();
/**
 * The list of functions to run with {@link DuESF.init}. You can add your own methods here.<br />
 * They're run after the settings have been loaded, but before everything else (especially the translator).
 * @type {function[]}
 */
DuESF.preInitMethods = [];
/**
 * The list of functions to run with {@link DuESF.init}. You can add your own methods here.<br />
 * They're run after the settings and translator have been loaded.
 * @type {function[]}
 */
DuESF.initMethods = [];
/**
 * The list of functions to run with {@link DuESF.enterRunTime}. You can add your own methods here.<br />
 * They're run after the UI has been created and just before runtime
 * @type {function[]}
 */
DuESF.enterRunTimeMethods = [];
/**
 * This method has to be called once at the very beginning of the script, just after the inclusion of DuESF <code>#include DuESF.jsxinc</code>
 * @param {DuESF.host} hostApplication - The host application of the script.
 * @param {string} [scriptName="DuESF"] - The name of your script, as it has to be displayed in the UI and the filesystem
 * @param {string} [scriptVersion="0.0.0"] - The version of your script, in the form "XX.XX.XX-Comment", for example "1.0.12-Beta". The "-Comment" part is optional.
 * @param {string} [companyName="RxLaboratory"] - The name of the company/organisation/individual developping this script.
 */
DuESF.init = function( scriptName, scriptVersion, companyName )
{
    var host = BridgeTalk.appName;

    if (host.indexOf('beta') == host.length -4) {
        DuESF.hostIsBeta = true;
        host = host.slice(0, host.length - 4);
    }

    DuESF.host = host;

    DuESF.hostVersion = new DuVersion( BridgeTalk.appVersion );
    if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS) {
        DuESF.hostVersion = new DuVersion( app.version );
    }
    
    DuESF.state = DuESF.State.INIT;

    scriptName = def( scriptName, "DuESF" );
    scriptVersion = def( scriptVersion, "0.0.0" );
    DuESF.scriptName = scriptName;
    DuESF.scriptVersion = new DuVersion(scriptVersion);
    DuESF.companyName = def( companyName, "RxLaboratory" );

    // Init settings
    DuSettings.init();

    // check if this script has already been run once
    // Check the version from latest run
    var prevVersion = DuESF.settings.get("version");
    if ( prevVersion == scriptVersion ) DuESF.scriptFirstRun = false;

    DuDebug.log( "DuESF: loading preinit methods." );

    // Pre translator init
    for( var i = 0; i < DuESF.preInitMethods.length; i++) {
        DuESF.preInitMethods[i]();
    }

    DuDebug.log( "DuESF: init translator." );

    // Init translator
    translationEngine.init();

    DuDebug.log( "DuESF: loading init methods." );

    // Init everything else
    for( var i = 0; i < DuESF.initMethods.length; i++) {
        DuESF.initMethods[i]();
    }
}

/**
 * This method has to be called once at the end of the script, when everything is ready and the main UI visible (after any prompt or setup during startup).
 */
DuESF.enterRunTime = function()
{
    DuDebug.log( "DuESF: Entering runtime..." );

    DuDebug.log( "DuESF: Getting script version..." );
    DuESF.settings.data.version = DuESF.scriptVersion.fullVersion;
    DuESF.settings.save();
    DuESF.state = DuESF.State.RUNTIME;

    // Everything which needs to be run once
    for( var i = 0; i < DuESF.enterRunTimeMethods.length; i++) {
        
        DuESF.enterRunTimeMethods[i]();
    }

    DuDebug.log( "DuESF: Runtime!" );
}

/**
 * Removes all DuESF parts from memory. Call this if you've updated before you reload DuESF.
 */
DuESF.delete = function()
{
    delete tr;
    delete def;
    delete jstype;
    delete DuBinary;
    delete DuColor;
    delete DuDate;
    delete DuDebug;
    delete DuDebugLog;
    delete DuESF;
    delete DuFile;
    delete DuFolder;
    delete DuList;
    delete DuMath;
    delete DuNumber;
    delete DuOCA;
    delete DuPath;
    delete DuProcess;
    delete DuProcessQueue;
    delete DuRegExp;
    delete DuRSS;
    delete DuRSSChannel;
    delete DuRSSItem;
    delete DuSettings;
    delete DuSystem;
    delete DuString;
    delete DuVersion;
    delete DuXML;
    delete DuXMP;
    delete DuZip;
    delete DuESF;
}

/**
 * Assigns a default value to a var if it's undefined.
 * @global
 * @param {object} val - The variable to set
 * @param {object} defaultVal - The default value
 * @return {object} The var
 * @example foo = def( foo, 12 ); // if foo was undefined, it is now 12, else it's unchanged.
 * @category DuESF
 */
function def( val, defaultVal )
{
	return isdef(val) ? val : defaultVal;
}

/**
 * Checks if a value is defined. Convenience replacement for `typeof val !== 'undefined`
 * @param {any} val The value
 * @return {boolean}
 * @category DuESF
 */
function isdef(val) {
    return typeof val !== 'undefined';
}

/**
 * Improved typeof which returns the type of object instead of 'object'
 * @global
 * @param {any} exp - The expression to check.
 * @return {string} The type always in lower case.
 * @category DuESF
*/
function jstype(value) {
    var type = typeof value;

    if (type == 'object') {
        try {
            type = value === null ? 'null' : Object.prototype.toString.call(value).match(/^\[object (.*)\]$/)[1].toLowerCase();
        } catch (e) {}
    }

    return type.toLowerCase();
}// ==================== |--------| ====================
// ==================== | system | ====================
// ==================== |--------| ====================

/**
* System tools
* @namespace
* @category DuESF
*/
var DuSystem = {};

(function()
{
    /**
        * The current OS, true if we're on Mac OS
        * @memberof DuSystem
        * @readonly
        * @type {boolean}
        */
    DuSystem.mac = $.os.toLowerCase().indexOf( "mac" ) >= 0;
    /**
        * The current OS, true if we're on Windows
        * @memberof DuSystem
        * @readonly
        * @type {boolean}
        */
    DuSystem.win = !DuSystem.mac ;

    /**
        * A string identifying the version of the OS; may vary depending on the host application
        * @memberof DuSystem
        * @readonly
        * @type {string}
        */
    DuSystem.osVersion = $.os.toLowerCase()
        .replace('windows/','')
        .replace('windows','')
        .replace('win', '')
        .replace('mac os','')
        .replace('macintosh','')
        .replace('mac','');
})();

/**
 * Opens a URL in the default browser.<br />
 * @param {string} url - The URL
 * @method
 */
DuSystem.openURL = function ( url )
{
    if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS)
    {   
        if ( DuSystem.mac )
        {
            system.callSystem( 'open "' + url + '"' );
        }
        else
        {
            system.callSystem( 'explorer "' + url + '"' );
        }
        return;
    }
    var timestamp = Date.now();
    var shortcut = new File(Folder.temp + '/duesf_shortcut_' + timestamp + '.url');
    shortcut.open('w');
    shortcut.writeln('[InternetShortcut]');
    shortcut.writeln('URL=' + url);
    shortcut.writeln();
    shortcut.close();
    shortcut.execute();
    shortcut.remove();
}

// ==================== |------| ====================
// ==================== | path | ====================
// ==================== |------| ====================

/**
	* Paths related methods
	* @namespace
	* @category DuESF
*/
var DuPath = {}

/**
 * Units to use for sizes.
 * @readonly
 * @enum {string}
 */
DuPath.SizeUnit = {
	BIT: "b",
	BYTE: "B",
	KB: "kB",
	MB: "MB",
	GB: "GB",
	TB: "TB"
}

/**
	* Joins multiple paths togetther.
	* @param {string[]}	 [parts]	- The parts to join togehter
	* @param {String}	[sep="/"]	- The separator to use. If not defined, will look for the first sep in the path.
	* @return {String} The final path
*/
DuPath.join = function(parts, sep)
{
   var separator = sep || false;
   if(!separator)
   {
       // Find first "/" or "\"
       if(parts[0].indexOf('/') != -1) separator = '/';
       if(parts[0].indexOf('\\') != -1) separator = '\\';
   }
   if(!separator) separator = '/';
   var replace   = new RegExp(separator+'{1,}', 'g');  // Replace ///// with /
   return parts.join(separator).replace(replace, separator);
}

/**
 * Converts a number of Bytes to kB or MB or GB or TB
 * For conversion to bits, a Byte is considered to be 8 bits.
 * @param {int} size The size in Bytes to convert
 * @param {DuPath.SizeUnit} [to=DuPath.SizeUnit.MB] The unit to convert to, one of: b, kB, MB, GB, TB
 * @param {int} [decimals=2] The number of decimals to round the result
 * @return {float} The result of the conversion
 */
DuPath.sizeFromBytes = function(size,  to, decimals )
{
	to = def(to, DuPath.SizeUnit.MB);
	decimals = def(decimals, 0);

	var result = size;
	if (to == DuPath.SizeUnit.BIT) result = result * 8;
	if (to == DuPath.SizeUnit.KB || to == DuPath.SizeUnit.MB || to == DuPath.SizeUnit.GB || to == DuPath.SizeUnit.TB) result = result / 1024;
	if (to == DuPath.SizeUnit.MB || to == DuPath.SizeUnit.GB || to == DuPath.SizeUnit.TB) result = result / 1024;
	if (to == DuPath.SizeUnit.GB || to == DuPath.SizeUnit.TB) result = result / 1024;
	if (to == DuPath.SizeUnit.TB) result = result / 1024;
	var round = 1;
	for (var i = 0; i < decimals; i++)
	{
		round = round * 10;
	}
	result = Math.round(result*round)/round;
	return result;
}

/**
	* Returns the name of the given path or file, as displayed by the filesystem
	* @example
	* DuPath.getName(new File("D:\\code\\open\\Duik\\shape test.test")) // "shape test.test"
	* DuPath.getName("D:/code/open/Duik/other shape.test") // "other shape.test"
	* @param {String|File|Folder}  [pathOrFile]	- The file or the path
	* @return {String} The basename
*/
DuPath.getName = function(pathOrFile)
{
    if (pathOrFile instanceof File || pathOrFile instanceof Folder) pathOrFile = pathOrFile.fsName;
	else {
		//make sure it's the fsName we use
		pathOrFile = new File(pathOrFile).fsName;
	}
    var name = pathOrFile.split("/").pop();
    name = name.split("\\").pop();
    return name;
}

/**
	* Returns the basename of the given path or file
	* @example
	* DuPath.getBasename(new File("D:/code/open/Duik/shape.test")) // "shape"
	* DuPath.getBasename("D:/code/open/Duik/shape.test") // "shape"
	* @param {String|File}	 [pathOrFile]	- The file or the path
	* @return {String} The basename
*/
DuPath.getBasename = function(pathOrFile)
{
    if (pathOrFile instanceof File) pathOrFile = pathOrFile.fsName;
	else {
		//make sure it's the fsName we use
		pathOrFile = new File(pathOrFile).fsName;
	}
    var name = pathOrFile.split("/").pop();
    name = name.split("\\").pop();
    if(name.lastIndexOf(".") > 0) return name.slice(0, name.lastIndexOf("."));
    return name;
}

/**
	* Returns the extension of the given path or file
	* @example
	* DuPath.getExtension(new File("D:/code/open/Duik/shape.other.test")) // "test"
	* DuPath.getExtension("D:/code/open/Duik/shape.test") // "test"
	* @param {String|File}	 [pathOrFile]	- The file or the path
	* @return {String} The extension
*/
DuPath.getExtension = function(pathOrFile)
{
	if (pathOrFile instanceof Folder) return '';
    if (pathOrFile instanceof File) pathOrFile = pathOrFile.absoluteURI;
    var name = pathOrFile.split("/").pop();
    name = name.split("\\").pop();
    if(name.indexOf(".") > 0) return name.split(".").pop();
    return '';
}

/**
	* Switch the extension of the given path. Create a new path from a given path and a given extension.
	* @param {String|File}	 [pathOrFile]	- The file or the path
	* @param {String}	     [newExtension]	- The new extension
	* @return {String}  The new path
*/
DuPath.switchExtension = function(pathOrFile, newExtension)
{
	// Remove leading .
	if (newExtension.indexOf('.') == 0) newExtension = newExtension.substring(1);
    if (pathOrFile instanceof File) pathOrFile = pathOrFile.absoluteURI;
    var point = pathOrFile.lastIndexOf(".");
    if (point == -1) return pathOrFile + "." + newExtension;
    return pathOrFile.slice(0, point) + "." + newExtension;
}

/**
 * Generates a new unique name for a file
 * @param {string} newName	- The wanted new name
 * @param {Folder} folder 	- The folder
 * @param {boolean} [increment=true] - true to automatically increment the new name if it already ends with a digit
 * @return {string}	The unique name, with a new number at the end if needed.
 */
DuPath.newUniqueName = function ( newName, folder ) {   
	var file = new File(folder.absoluteURI + "/" + newName);
	while (file.exists)
	{
		file = new File(folder.absoluteURI + "/" + DuPath.incrementName(file) );
	}
	return DuPath.getName(file);
}

/**
 * Increments the last number before the extension in a filename
 * @param {File} file The file
 * @return {string} The incremented fileName
 */
DuPath.incrementName = function (file) {

	var ext = DuPath.getExtension(file);
	var baseName = DuPath.getBasename(file);

	var regex = /(\d*)$/
	var match = baseName.match(regex);
	var num = '_01';
	var numDigits = 0;
	if (match) {
		var n = match[0];
		numDigits = n.length;
		var n = parseInt(n);
		if (!isNaN(n)) {
			n++;
			num = DuNumber.toString(n,numDigits);
		}
	}

	return baseName.substring(0, baseName.length - numDigits) + num + '.' + ext;

}

/**
 * Removes all forbidden characters from a string to be used as a filename
 * @param {string} name - The filename to fix
 * @param {string} [placeholder='.'] - The string used to replace the forbidden characters
 * @return {string} The fixed filename
 */
DuPath.fixName = function (name, placeholder ) {
	placeholder = def( placeholder, '.');
	
	return name.replace( /([\/\\:*?"<>|])/g, placeholder);
}

/**
 * Checks if the given path represents an existing file
 * @param {File|Folder|string} path The path to check
 * @return {bool}
 */
DuPath.isFile = function(path) {
	if (jstype(path) == 'string')
	{
		path == new File(path);
		if (path.exists) return true;
		return false;
	}
	if (path instanceof File) return path.exists;
	return false;
}

/**
 * Checks if the given path represents an existing folder
 * @param {File|Folder|string} path The path to check
 * @return {bool}
 */
DuPath.isFolder = function(path) {
	if (jstype(path) == 'string')
	{
		path == new Folder(path);
		if (path.exists) return true;
		return false;
	}
	if (path instanceof Folder) return path.exists;
	return false;
}// ==================== |--------| ====================
// ==================== | folder | ====================
// ==================== |--------| ====================


/**
 * Folder related methods
 * @namespace
 * @category DuESF
 */
var DuFolder = {};

/**
 * Recursively remove all content from folder and the folder itself. Warning, this does not move files to the recycle bin and can not be undone.
 * @param {string|Folder} folder - the path or Folder object to wipe.
 */
DuFolder.wipeFolder = function(folder)
{
	if ( jstype(folder) == 'string' && folder != '') folder = new Folder(folder);
	if ( !folder.exists ) return;

	//get content and remove 
	var files = folder.getFiles();
	var it = new DuList(files);
	it.do(function (file)
	{
		if ( jstype(file) == 'folder' ) DuFolder.wipeFolder(file);
		else file.remove();
	});
	folder.remove();
}

/**
	* Recursively gets all files in a folder using a name filter
	* Returns an array of File objects.
	* @param {Folder}	folder	- The Folder
	* @param {string|function}	[filter="*"]	- A search mask for file names, specified as a string or a function.
	* A mask string can contain question mark (?) and asterisk (*) wild cards. Default is "*", which matches all file names.
	* Can also be the name of a function that takes a File or Folder object as its argument. It is called for each file or folder found in the search; if it returns true, the object is added to the return array.
	* @return {Array}	The files found.
*/
DuFolder.getFiles = function (folder,filter)
{
	function isFolder(f) { return f instanceof Folder; }

	if (!isdef( folder )) return [];
	if (!(folder instanceof Folder)) return [];

	var files = folder.getFiles(filter);
	if (files === null) files = [];

	var folders = folder.getFiles( isFolder );

	for (var i = 0, num = folders.length ; i < num ; i++)
	{
		files = files.concat(DuFolder.getFiles(folders[i],filter));
	}
	return files;
}

/**
 * Tests if a folder can be read
 * @param {Folder|string} folder The folder
 * @return {bool}
 */
DuFolder.canRead = function (folder) {
	if ( jstype(folder) == 'string' && folder != '') folder = new Folder(folder);
	if ( !folder.exists ) return false;

	function isFile(f) { return f instanceof File; }

	// Try to list files
	try {
		f = folder.getFiles( isFile );
		/*if (f.length > 0) {
			if (f[0].open('r')){
				f.read();
				f.close();
				return true;
			}
			return false;
		}//*/
		return true;
	}
	catch (e) { return false; }
}

/**
 * Tests if a folder can be written
 * @param {Folder|string} folder The folder
 * @return {bool}
 */
DuFolder.canWrite = function (folder) {
	if ( jstype(folder) == 'string' && folder != '') folder = new Folder(folder);
	if ( !folder.exists ) return false;

	var content = 'test-write-access';
	var f = new File(folder.absoluteURI + '/' + content);

	if (f.open('w')) {
		try {
			f.write(content);
			f.close();
			f.remove();
			return true;
		}
		catch(e) { return false };
	} else return false;
}

// Low-level undocumented function. Called by DuESF.init()
DuFolder.init = function()
{
	DuFolder.duesfData = new Folder( DuPath.join( [
		DuFolder.userData.absoluteURI,
		DuESF.companyName,
		'AdobeScripts',
		DuESF.scriptName
		] ) );

}
DuESF.initMethods.push( DuFolder.init );

/**
 * The default user data folder.<br />
 * Replaces the Folder.userData provided by ESTK which does not work properly with mac network sessions.<br />
 * In windows, the value of %APPDATA% ("C:\Documents and Settings\username\Application Data")<br />
 * In Mac OS, "~/Library/Application Support"
 * @type {Folder}
 */
DuFolder.userData = Folder.userData;
if (DuSystem.mac) DuFolder.userData = new Folder('~/Library/Application Support');
// We absolutely need write access... If not, use a temp folder
if (!DuFolder.canWrite(DuFolder.userData)) DuFolder.userData = Folder.temp;

DuFolder.duesfData = new Folder( DuPath.join( [
		DuFolder.userData.absoluteURI,
		DuESF.companyName,
		'AdobeScripts',
		DuESF.scriptName
		] ) );
// ==================== |--------| ====================
// ==================== | object | ====================
// ==================== |--------| ====================

/**
 * JS Objects related methods
 * @namespace
 * @category DuESF
 */
var DuJSObj = {};

/**
 * Sorts object keys and returns a new sorted object.
 * @param {object} obj The object to sort.
 * @param {bool} [reverse=false] Set to true to sort in the reverse order.
 * @param {function} [compareFn] Specifies a function that defines the sort order. Note that this parameter is ignored if reversed is true.<br />
 * If omitted, the keys are converted to strings, then sorted according to each character's Unicode code point value.
 * @return {object} The new sorted object.
 */
DuJSObj.sort = function( obj, reverse, compareFn ) {
    reverse = def(reverse, false);

    if (reverse) {
        compareFn = function(a, b) {
            if (b > a) return 1;
            if (b < a) return -1;
            if (b == a) return 0;
        }
    }

    var newObj = {};
    var tempArray = [];
    for (i in obj) {
        tempArray.push(i);
    }

    if (isdef( compareFn )) tempArray.sort(compareFn);
    else tempArray.sort();

    for (var i = 0, n = tempArray.length; i < n; i++)
    {
        var key = tempArray[i];
        newObj[key] = obj[key];
    }

    return newObj;
}

/**
 * Deletes all properties from an object, except a predefined list.
 * @param {object} obj The object to clear.
 * @param {string[]} [ignoredProperties] The properties to keep
 */
DuJSObj.clear = function( obj, ignoredProperties ) {
    ignoredProperties = def(ignoredProperties, []);
    // Remove existing items
    for (i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        // ignore properties
        var ok = true;
        for (var j = 0, n = ignoredProperties.length; j < n; j++)
        {
            if (i == ignoredProperties[j]) {
                ok = false;
                break;
            }
        }
        if (ok) delete obj[i];
    }
}

DuJSObj.equals = function( obj1, obj2 ) {
    for( key in obj1 ) {
        if (obj2[key] !== obj1[key]) return false
    }
    return true;
}// ==================== |-------| ====================
// ==================== | debug | ====================
// ==================== |-------| ====================

/**
* Tools for debugging
* @namespace
 * @category DuESF
*/
var DuDebug = {};

/**
 * Set this attribute to a DuDebugLog you have created to automatically add some debug infos to the log (like catched errors).
 * @type {DuDebugLog|null}
 */
DuDebug.debugLog = null;

/**
 * The log levels
 * @enum {int}
 * @readonly
 */
DuDebug.LogLevel =
{
	VERBOSE: 0,
	DEBUG: 1,
	WARNING: 2,
	CRITICAL: 3,
	FATAL: 4,
	NO_DEBUG: 5
}

/**
 * Logs a message to DuDebug.DuDebugLog if it has been set.
 * @param {string} message - The message to log
 * @param {DuDebug.LogLevel} [level=DuDebug.LogLevel.DEBUG] - The level of the message
 */
DuDebug.log = function (message,level)
{
	if ( DuDebug.debugLog != null ) DuDebug.debugLog.log( message,level );
}

/**
 * This methods shows an alert with an error thrown if DuESF.debug is set to true, and logs it in DuESF.debug.DuDebugLog if it has been set.<br />
 * The error is actually thrown only if JS Debugging is enabled in the host application, otherwise it is just shown in an alert.
 * @param {Error|string} error - An error thrown and catched
 */
DuDebug.error = function (error)
{
	var file = new File( error.fileName );
	var errorString = 'An error has occured in file\n\n' + file.name + '\n\nlocated in:\n' + file.parent.fsName + '\n\nat line ' + error.line + '\n\n' + error.description;
	DuDebug.log( errorString, DuDebug.LogLevel.WARNING );
	if ( DuESF.debug )
	{
		alert( errorString );
		// Throw if debugger enabled
		if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS)
			if (DuAE.isDebuggerEnabled()) throw error;
	}
}

/**
 * Runs a method/script safely, i.e. enclosed in a try...catch, and logs any error thrown.<br />
 * arguments can be added after the first parameter, they will be passed to the callback.<br />
 * The callback is enclosed in a function to make sure it does not create global variables.
 * @param {function|string} callback - The method to run, or a script as a string
 * @return {any} The return of the callback, null if it fails.
 */
DuDebug.safeRun = function (callback)
{
	if (!isdef( callback )) throw "Error: DuDebug.safeRun(callback): Invalid parameter: callback cannot be undefined";
	if (callback == '') throw "Error: DuDebug.safeRun(callback): Invalid parameter: callback cannot be an empty string";
	var call = '';
	if (jstype(callback) != 'string' && arguments.legnth > 1)
	{
		var args = [];
		for (var i = 1, num = arguments.length; i < num; i++)
		{
			args.push(arguments[i]);
		}
		var params = args.join(',');
		call = 'var result = callback(' + params + '); result;'
	}
	else if (jstype(callback) == 'string') call = callback;
	
	try
	{
		if (call != '')
		{
			(function(){
				return eval(call);
			})();
		}
		else 
		{
			return callback();
		}
	}
	catch ( error )
	{
		DuDebug.error(error);
		return null;
	}
}

/**
 * Inspects a javascript object and outputs all of its attributes
 * @param {object} obj - The object to inspect.
 * @param {boolean} [ownProperties=true] - Whether to inspect only the own properties of the object.
 * @return {string} The report. 
 */
DuDebug.inspect = function(obj, ownProperties)
{
	ownProperties = def( ownProperties, true );

	var report = "";
	for (var key in obj)
	{
		if (ownProperties && !obj.hasOwnProperty(key)) continue;
		report += key + " | type: " + typeof(obj[key]) + " | value: " + obj[key].toString() + "\n";
	}
	return report;
}

/**
 * Alerts a nice TypeError description, if {@link DuESF.debug} is true, and logs it if there's a log.<br />
 * Note that it does not actually throws an error.
 * @param {any} variable The failing argument
 * @param {string} varName The failing argument name
 * @param {string} varType The expected type
 * @param {string} [functionName] The name or description of the function throwing the error.
 * @return {string} The (multiline) description for the error
 */
DuDebug.throwTypeError = function (variable, varName, varType, functionName)
{
	functionName = def (functionName, '');
	if (typeof functionName === 'Function') {
		functionName = func.toString().split('\n')[0];
	}

	var e = 'TypeError:\n';
	if (functionName != '') e += "in function " + functionName + "\n";
	e += "\n" + varName + " should be of type " + varType + "\n";
	e += "but it is of type " + jstype(variable);
	if (isdef( variable )) try { e += " and has value: " + variable.toSource(); } catch(e) {};

	//ADD stack trace
	e += '\n\nStack:\n' + $.stack;
		
	DuDebug.log(e, DuDebug.LogLevel.CRITICAL);

	if ( DuESF.debug )
	{
		alert( e );
		// Throw if debugger enabled
		if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS)
			if (DuAE.isDebuggerEnabled()) throw e;
	}

	return e;
}

/**
 * Alerts a nice UndefinedError description, if {@link DuESF.debug} is true, and logs it if there's a log..<br />
 * Note that it does not actually throws an error.
 * @param {any} varName The name of the arg which should not be undefined
 * @param {any} [functionName] The name or description of the function throwing the error.
 * @return {string} The (multiline) description for the error
 */
DuDebug.throwUndefinedError = function( varName, functionName )
{
	if (!DuESF.debug) return;

	functionName = def (functionName, '');
	if (typeof functionName === 'Function') {
		functionName = func.toString().split('\n')[0];
	}

	var e = 'UndefinedError:\n';
	if (functionName != '') e += "in function " + functionName + "\n";
	e += "\n" + varName + " cannot be undefined.";

	DuDebug.log(e, DuDebug.LogLevel.CRITICAL);

	if ( DuESF.debug )
	{
		alert( e );
		// Throw if debugger enabled
		if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS)
			if (DuAE.isDebuggerEnabled()) throw e;
	}

	return e;
}

/**
 * Alerts a nice Error description, if {@link DuESF.debug} is true, and logs it if there's a log..<br />
 * Note that it does not actually throws an error.
 * @param {any} message The message to show
 * @param {any} [functionName] The name or description of the function throwing the error.
 * @param {Error} [error] A JS error.
 * @param {Boolean} [neverThrow=false] If true, will prevent actually throwing the error even if the debugger is enabled.
 * @return {string} The (multiline) description for the error
 */
DuDebug.throwError = function( message, functionName, error, neverThrow )
{
	functionName = def (functionName, '');
	neverThrow = def(neverThrow, false);
	if (typeof functionName === 'Function') {
		functionName = func.toString().split('\n')[0];
	}

	var e = 'Error:\n';
	if (functionName != '') e += "in function " + functionName + "\n";
	e += "\n" + message;

	if (isdef( error ))
	{
		var file = new File(error.fileName);
		e += '\n\nIn file\n\n' + file.name + '\n\nlocated in:\n' + file.parent.fsName + '\n\nat line ' + error.line + '\n\n' + error.description;
	}

	DuDebug.log(e, DuDebug.LogLevel.CRITICAL);

	if ( DuESF.debug )
	{
		alert( e );
		if (!neverThrow)
		{
			// Throw if debugger enabled
			if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS)
				if (DuAE.isDebuggerEnabled()) 
				{
					if (isdef( error )) throw error;
					else throw e;
				}
		}
	}

	return e;
}

/**
 * Checks if the given variable has the correct type,<br />
 * alerts a nice error if not using {@link DuDebug.throwUndefinedError()} or {@link DuDebug.throwTypeError()}.
 * @param {any} variable The variable to check
 * @param {string} argName  The variable name
 * @param {string} [argType] The expected type name (as return by {@link jstype()}). If not provided, checks only if the var is undefined
 * @param {any} [functionName] The name or description of the function throwing the error.
 * @return {string|bool} The (multiline) description for the error if any and {@link DuESF.debug} is true, false if there's an error and {@link DuESF.debug} is false, true if everything is ok.
 */
DuDebug.checkVar = function ( variable, varName, varType, functionName )
{
	// Check if undefined
	if ( !isdef( variable ) )
	{
		if (DuESF.debug) return DuDebug.throwUndefinedError( varName, functionName );
		else return false;
	} 
	if (isdef( varType ))
		if ( jstype( variable ) != varType )
		{
			if (DuESF.debug) return DuDebug.throwTypeError( variable, varName, varType, functionName );
			else return false;
		}
	return true;
}

/**
 * Constructs a new debug logger
 * @class DuDebugLog
 * @classdesc A logger for debugging purposes: writes debug messages to a file, with date and type
 * @param	{string|File} [pathOrFile] - The log file. By default, located next to the script settings file
 * (if constructed <strong>after</strong> {@link DuESF.init()}).
 * @param	{boolean} [clear=true] - Whether to clear the previous log file before starting
 * @param	{DuDebug.LogLevel}	[logLevel=DuDebug.LogLevel.DEBUG] -  The log level.
 * @param	{boolean}	[enabled=DuESF.debug] -  true to enable the log and start recording
 * @property {boolean}	running - true if the timer is running, false if it is stopped.
 * @category DuESF
 */
function DuDebugLog(pathOrFile,clear,logLevel,enabled)
{
	/**
	 * The log file.
	 * @memberof DuDebugLog
	 * @readonly
	 * @name file
	 * @type {File}
	 */
	this.file = null;

	if (!isdef( pathOrFile ))
	{
		var folder = DuESF.scriptSettings.file.parent;
		pathOrFile = new File( folder.absoluteURI + '/' + DuESF.scriptName + '_debug_log.txt');
	}

	if (!(pathOrFile instanceof File)) this.file = new File(pathOrFile);
	else this.file = pathOrFile;
	this.file.encoding = 'UTF-8';

	/**
	 * The log level.
	 * @memberof DuDebugLog
	 * @type {DuDebug.LogLevel}
	 * @name level
	 * @default DuDebug.LogLevel.DEBUG
	 */
	this.level = def (logLevel, DuDebug.LogLevel.DEBUG);

	/**
	 * true to enable the log and record logs.
	 * @memberof DuDebugLog
	 * @type {DuDebug.LogLevel}
	 * @name enabled
	 * @default DuESF.debug
	 */
	this.enabled = def( enabled, DuESF.debug );

	clear = def (clear, true);
	var append = !clear && this.file.exists;

	if (this.level <= DuDebug.LogLevel.DEBUG)
	{
		var currentDate = new Date();
		DuFile.writeln (
			this.file,
			'[' + currentDate.toString() + '] == Debugger started. ==',
			append );
	}

	this.elapsed = 0;
	this.running = false;

	this.log( "\n=========== NEW LOG =============\n=================================" , DuDebug.LogLevel.NO_DEBUG);
}

/**
	* Logs a message
	* @param {string} message - The message to log
	* @param {DuDebug.LogLevel} [level=DuDebug.LogLevel.DEBUG] - The level of the message
	*/
DuDebugLog.prototype.log = function (message,level)
{
	level = def( level, DuDebug.LogLevel.DEBUG );

	if (!this.enabled) return;
	if (level < this.level) return;

	var currentDate = new Date();
	var levelString = " || Debug info || ";
	if (level == DuDebug.LogLevel.WARNING) levelString = " || Warning || ";
	else if (level == DuDebug.LogLevel.CRITICAL) levelString = " || Critical || ";
	else if (level == DuDebug.LogLevel.FATAL) levelString = " || Fatal || ";

	DuFile.writeln (
			this.file,
			'[' + currentDate.toString() + ']' + levelString + message,
			true );
}

/**
	* Starts the debugger timer.
	* @param {string} [message] - A message to display in the log
	* @param {DuDebug.LogLevel} [level=DuDebug.LogLevel.VERBOSE] - The level of the message
	*/
DuDebugLog.prototype.startTimer = function(message, level)
{
	if (!this.enabled) return;

	level = def(level, DuDebug.LogLevel.VERBOSE );
	message = def (message, '');

	if (level >= this.level)
	{
		DuDebug.safeRun( function()
		{
			if (this.file.open('a'))
			{
				var currentDate = new Date();
				this.file.writeln('[' + currentDate.toString() + '] == Timer started. == ' + message);
				this.file.close();
			}
		});
	}

	if (!this.running) this.elapsed = 0;
	this.running = true;
	$.hiresTimer;
}

/**
	* Stops the debugger timer.
	* @param {string} [message] - A message to display in the log
	* @param {DuDebug.LogLevel} [level=DuDebug.LogLevel.DEBUG] - The level of the message
	*/
DuDebugLog.prototype.stopTimer = function(message, level)
{
	if (!this.enabled) return;
	if (!this.running) return;

	level = def( level, DuDebug.LogLevel.DEBUG );

	this.elapsed += Math.round($.hiresTimer/1000);

	if (!isdef( message )) message = '';

	if (level >= this.level)
	{
		DuDebug.safeRun( function()
		{
			if (this.file.open('a'))
			{
				var currentDate = new Date();
				this.file.writeln('[' + currentDate.toString() + '] == Timer stopped == Time elapsed | ' + this.elapsed + 'ms | == ' + message);
				this.file.close();
			}
		});
	}

	this.elapsed = 0;
	this.running = false;
	$.hiresTimer;
}

/**
	* Checks the time elapsed since the timer has started<br />
	* If the timer is not running, it will be started.
	* @param {string} message - A message to display in the log
	* @param {DuDebug.LogLevel} [level=DuDebug.LogLevel.DEBUG] - The level of the message
	* @return {int} The time elapsed in milliseconds
	*/
DuDebugLog.prototype.checkTimer = function(message, level)
{
	level = def( level, DuDebug.LogLevel.DEBUG );

	if (!this.running) this.startTimer();
	this.elapsed += Math.round($.hiresTimer/1000);

	if (!this.enabled) return -1;

	if (level >= this.level)
	{
		DuDebug.safeRun(function()
		{
			if (this.file.open('a'))
			{
				message = def(message, '');

				var currentDate = new Date();
				this.file.writeln('[' + currentDate.toString() + '] == Time elapsed | ' + this.elapsed + 'ms | == ' + message);
				this.file.close();
			}
		});
		
	}

	return this.elapsed;
}
// ==================== |------| ====================
// ==================== | list | ====================
// ==================== |------| ====================

/**
 * Constructs a new DuList object
 * @class
 * @name DuList
 * @classdesc DuList helps manipulating JS Arrays and After Effects Collections the same way. It is faster than converting a Collection to an Array.<br />
 * Other specific Adobe Lists in other Applications may be supported later.<br />
 * The first item of a DuList is always at index 0, the last at index length-1.<br />
 * Accessor is the {@link DuList#at DuList.at()} method instead of <code>Array</code>.<br />
 * List length is given by the {@link DuList#length DuList.length()} method instead of a property.<br />
 * DuList does not reimplement methods to add or remove items, use the source list instead with the {@link DuList#list DuList.list()} property
 * and the original methods (from the <code>Collection</code> instance or <code>Array</code>).<br />
 * DuList can also be used as an iterator with iterator-like methods like {@link DuList#next DuList.next()}.<br />
 * You can also run a function on each item with {@link DuList#do DuList.do()}.
 * @property {boolean} isCollection - true if the original list is an After Effects Collection, false otherwise
 * @property {boolean} isArray - true if the original  list is an Array, false otherwise
 * @category DuESF
 */
function DuList(obj) {
    obj = def(obj, []);
    var newList = obj;
    // Copy constructor
    if (obj instanceof DuList)
        newList = obj.list;

    /**
     * True if the original list is an After Effects Collection, false otherwise.
     * @memberof DuList.prototype
     * @name isCollection
     * @readonly
     * @type {Boolean}
     */
    this.isCollection = false;
    /**
     * True if the original list is an Array, false otherwise
     * @memberof DuList.prototype
     * @name isArray
     * @readonly
     * @type {Boolean}
     */
    this.isArray = true;

    //check if list is an Array or a Collection
    if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS) {
        if (
            newList instanceof ItemCollection ||
            newList instanceof LayerCollection ||
            newList instanceof OMCollection ||
            newList instanceof RQItemCollection
        ) {
            this.isCollection = true;
            this.isArray = false;
        }
    }

    //if not a list, store it as a one-item array
    if (!(
            newList instanceof Array ||
            newList instanceof ItemCollection ||
            newList instanceof LayerCollection ||
            newList instanceof OMCollection ||
            newList instanceof RQItemCollection ||
            newList instanceof DuList
        )) {
        newList = [newList];
    }

    /**
     * The original list, an Array or an After Effects Collection.
     * @memberof DuList.prototype
     * @name list
     * @readonly
     * @type {Array|Collection}
     */
    this.list = newList;
    /**
     * The current index of the iterator.
     * @memberof DuList.prototype
     * @name current
     * @readonly
     * @type {int}
     */
    this.current = -1;
    /**
     * True if the iterator is at the start.
     * @memberof DuList.prototype
     * @name atStart
     * @readonly
     * @type {Boolean}
     */
    this.atStart = false;
    /**
     * True if the iterator has reached the end.
     * @memberof DuList.prototype
     * @name atEnd
     * @readonly
     * @type {Boolean}
     */
    this.atEnd = false;
    /**
     * True if the iterator is between 0 and length-1. Note that on creation, the iterator is always invalid, but calling {@link DuList#next DuList.next()} moves it to the beginning and makes it valid.
     * @memberof DuList.prototype
     * @name valid
     * @readonly
     * @type {Boolean}
     */
    this.valid = false;
}

/**
 * Returns the number of items in the list
 * @return {int} The number of items.
 */
DuList.prototype.length = function() {
    return this.list.length;
}

/**
 * Alias for {@link DuList#length DuList.length()}
 * @alias DuList.length()
 * @name count
 * @memberof DuList.prototype
 * @function
 */
DuList.prototype.count = DuList.prototype.length;

/**
 * Accessor. First item is always 0, last is always length()-1<br />
 * Does not move the iterator to the index; to move the iterator use {@link DuList.goTo} instead.
 * @param {int} index The index of the item. If it is out of range (negative or > length()-1), returns null
 * @return  {object} The item at the given index.
 */
DuList.prototype.at = function(index) {
    var count = this.length();
    if (count == 0) return null;

    var min, max;

    if (this.isCollection) {
        min = 1;
        index++;
        max = count;
    } else {
        min = 0;
        max = count - 1;
    }

    if (index < min) return null;
    if (index > max) return null;

    return this.list[index];
}

/**
 * Gets the first index of a value, or -1 if not found
 * @memberof DuList
 * @param {*}	value	- The value to find. Must be compatible with the == operand if you don't provide a comparison function
 * @param {function}	[comparisonFunction]	- A function which compares two values which returns true if the values are considered the same.
 * @return {int}	The index of the value, -1 if not found
 */
DuList.prototype.indexOf = function(value, comparisonFunction) {
    if (DuDebug.checkVar(value, 'value', undefined, 'DuList.indexOf( value, comparisonFunction )') !== true)
        return -1;

    var useFunction = isdef( comparisonFunction );

    for (var i = 0, n = this.length(); i < n; i++) {
        if (!useFunction && this.at(i) == value) return i;
        else if (useFunction && comparisonFunction(this.at(i), value)) return i;
    }
    return -1;
}

/**
 * Checks if the list contains the item
 * @memberof DuList
 * @param {*}	value	- The value to find. Must be compatible with the == operand if you don't provide a comparison function
 * @param {function}	[comparisonFunction]	- A function which compares two values which returns true if the values are considered the same.
 * @return {Boolean}	
 */
DuList.prototype.contains = function(value, comparisonFunction) {
    return this.indexOf(value, comparisonFunction) >= 0;
}

/**
 * Checks if the list has duplicated values
 * @memberof DuList
 * @return {boolean}	true if the list has duplicated values. Always false for Ae Collections as they can't have duplicated items.
 */
DuList.prototype.hasDuplicates = function() {
    for (var i = 0, n = this.length(); i < n; i++) {
        for (var j = i + 1; j < n; j++) {
            if (this.at(i) === this.at(j)) return true;
        }
    }
    return false;
}

/**
 * Returns all duplicated values found in the list
 * @memberof DuList
 * @return {DuList}	The duplicated values. An empty list for Ae Collections as they can't have duplicated items.
 */
DuList.prototype.getDuplicates = function() {
    if (this.isCollection) return new DuList();

    var duplicates = [];

    for (var i = 0, n = this.length(); i < n; i++) {
        for (var j = i + 1; j < n; j++) {
            if (this.at(i) === this.at(j)) duplicates.push(this.at(j));
        }
    }

    duplicates = new DuList(duplicates);
    duplicates.removeDuplicates(duplicates);

    return duplicates;
}

/**
 * Removes all duplicated values from the list, and returns them.<br />
 * The internal list may be converted to an Array if needed.<br />
 * The iterator invalidated.
 * @memberof DuList
 * @param {function}	[comparisonFunction] - A function which compares two values which returns true if the values are considered the same.
 * @return {Array}	The duplicated (and removed) values. An empty list for Ae Collections as they can't have duplicated items.
 */
DuList.prototype.removeDuplicates = function(comparisonFunction) {
    // Convert to an Array
    this.convertToArray();

    var useFunction = isdef( comparisonFunction );
    var removed = [];

    for (var i = 0, n = this.length(); i < n; i++) {
        for (var j = n - 1; j > i; j--) {
            if (!useFunction && this.at(i) == this.at(j)) {
                removed = removed.concat(this.list.splice(j, 1));
            } else if (useFunction && comparisonFunction(this.at(i), this.at(j))) {
                removed = removed.concat(this.list.splice(j, 1));
            }
        }
    }

    this.valid = false;
    this.current = -1;

    return new DuList(removed);
}

/**
 * Compares two arrays.<br />
 * The items in the arrays must be compatible with the == operand if you don't provide a comparison function
 * @memberof DuList
 * @param {Array|Collection|DuList} other - The other list
 * @param {int} [floatPrecision=-1] - The precision for (float) number comparison, number of decimals. Set to -1 to not use.
 * @return {boolean} true if the two arrays contain the same values
 */
DuList.prototype.equals = function(other, comparisonFunction, floatPrecision) {
    var useFunction = typeof comparisonFunction === 'function';

    if (!DuList.isList(other)) return false;

    other = new DuList(other);

    if (this.length() != other.length()) return false;

    var originalIndex = this.current;

    this.reinitIterator();
    var ok = true;
    var item1;
    while (item1 = this.next()) {
        var item2 = other.at(this.current);

        // A list, recursive
        if (DuList.isList(item1)) {
            var test = new DuList(item1);
            if (!test.equals(item2, comparisonFunction, floatPrecision)) {
                ok = false;
                break;
            }
            continue;
        }

        // Comparison function
        if (useFunction) {
            if (!comparisonFunction(item1, item2)) {
                ok = false;
                break;
            }
            continue;
        }

        // Numbers
        if (jstype(item1) == 'number') {
            if (!DuMath.equals(item1, item2, floatPrecision)) {
                ok = false;
                break;
            }
            continue;
        }

        // Other
        if (item1 != item2) {
            ok = false;
            break;
        }
    }

    // Restore original iterator index
    this.goTo(originalIndex);
    return ok;
}

/**
 * Returns true if the list is empty
 * @return {boolean}
 */
DuList.prototype.isEmpty = function() {
    return this.length() == 0;
}

/**
 * Gets the last item in the list
 * @return {*} The last item, null if the list is empty.
 */
DuList.prototype.last = function() {
    if (this.isEmpty()) return null;
    return this.at(this.length() - 1);
}

/**
 * Gets the first item in the list
 * @return {*} The first item, null if the list is empty.
 */
DuList.prototype.first = function() {
    if (this.isEmpty()) return null;
    return this.at(0);
}

/**
 * Converts the stored list to an Array if it was a Collection.<br />
 * This enables the modification of the list, but changes in the list will not be reflected in the application (if a layer is removed from the list, it is not removed in the application).
 */
DuList.prototype.convertToArray = function(index) {
    // Nothing to do
    if (!this.isCollection) return;

    // The new array
    var arr = [];
    for (var i = 1, n = this.list.length; i <= n; i++) {
        arr.push(this.list[i]);
    }

    this.isCollection = false;
    this.isArray = true;
    this.list = arr;

}

/**
 * Removes the item at the given index. The internal list may be converted to an Array if needed.<br />
 * The iterator invalidated.
 * @param {int} index The index.
 */
DuList.prototype.remove = function(index) {
    // Convert to an Array
    this.convertToArray();
    this.list.splice(index, 1);

    this.valid = false;
    this.current = -1;
}

/**
 * Removes the first value from the list. The value must be checkable with the <code>==</code> operator if no <code>comparisonFunction</code> is provided.<br />
 * The internal list may be converted to an Array if needed.<br />
 * The iterator invalidated.
 * @param {int} index The index.
 * @param {function} [comparisonFunction] A function which compares two values which returns true if the values are considered the same.
 */
DuList.prototype.removeOne = function(value, comparisonFunction) {
    // Convert to an Array
    this.convertToArray();

    var index = this.indexOf(value, comparisonFunction);
    if (index >= 0) this.remove(index);

    this.valid = false;
    this.current = -1;
}

/**
 * Removes all occurences of the value from the list. The value must be checkable with the <code>==</code> operator if no <code>comparisonFunction</code> is provided.<br />
 * The internal list may be converted to an Array if needed.<br />
 * The iterator invalidated.
 * @param {int} index The index.
 * @param {function} [comparisonFunction] A function which compares two values which returns true if the values are considered the same.
 * @return {int} The number of items removed.
 */
DuList.prototype.removeAll = function(value, comparisonFunction) {
    // Convert to an Array
    this.convertToArray();

    var count = 0;
    var index;
    do {
        index = this.indexOf(value, comparisonFunction);
        if (index >= 0) this.remove(index);
        count++;
    }
    while (index >= 0);

    this.valid = false;
    this.current = -1;

    return count;
}

/**
 * Reimplements the Array.sort() method.<br />
 * The internal list may be converted to an Array if needed.<br />
 * The iterator is invalidated.
 * @param {function} compareFunction Specifies a function that defines the sort order. If omitted, the array elements are converted to strings, then sorted according to each character's Unicode code point value.
 * @return {Array} The sorted array. Note that the array is sorted in place, and no copy is made.
 */
DuList.prototype.sort = function(compareFunction) {
    // Convert to an Array
    this.convertToArray();

    if (!isdef( compareFunction )) this.list.sort();
    else this.list.sort(compareFunction);

    this.valid = false;
    this.current = -1;

    return this.list;
}

/**
 * Adds one or more elements to the end of the list and returns the new length of the list.<br />
 * The internal list may be converted to an Array if needed.
 * @param {any} element0 The element(s) to add to the end of the array.
 * @param {any} element1 The element(s) to add to the end of the array.
 * @param {any} element... The element(s) to add to the end of the array.
 * @param {any} elementN The element(s) to add to the end of the array.
 * @return {int} The new length of the list.
 */
DuList.prototype.push = function() {
    this.convertToArray();

    for (var i = 0, n = arguments.length; i < n; i++) {
        this.list.push(arguments[i]);
    }

    return this.list.length;
}

/**
 * Mergeserge two lists.<br />
 * This method does not change the existing list, but instead returns a new list. 
 * @param {Array|DuList} other The other list.
 * @return {DuList} The new list.
 */
DuList.prototype.concat = function(other) {
    // Make copies to not modify existing lists
    other = new DuList(other);
    current = new DuList(this);
    // Convert to arrays
    other.convertToArray();
    current.convertToArray();
    return new DuList( current.list.concat(other.list) );
}

/**
 * Adds one or more elements to the end of the list and returns the new length of the list, only if the value is not already in the list<br />
 * The internal list may be converted to an Array if needed.
 * @param {function} [comparisonFunction] A function which compares two values which returns true if the values are considered the same.
 * @param {any} element0 The element(s) to add to the end of the array.
 * @param {any} element1 The element(s) to add to the end of the array.
 * @param {any} element... The element(s) to add to the end of the array.
 * @param {any} elementN The element(s) to add to the end of the array.
 * @return {int} The new length of the list.
 */
DuList.prototype.pushUnique = function(comparisonFunction) {
    this.convertToArray();

    var args = [];

    for (var i = 1, n = arguments.length; i < n; i++) {
        var arg = arguments[i];
        if (this.indexOf(arg, comparisonFunction) >= 0) continue;
        this.push(arg);
    }

    return this.list.length;
}

/**
 * Alias for {@link DuList#push DuList.push()}
 * @alias DuList.push()
 * @name append
 * @memberof DuList.prototype
 * @function
 */
DuList.prototype.append = DuList.prototype.push;

/**
 * Alias for {@link DuList#pushUnique DuList.pushUnique()}
 * @alias DuList.pushUnique()
 * @name appendUnique
 * @memberof DuList.prototype
 * @function
 */
DuList.prototype.appendUnique = DuList.prototype.pushUnique;

/**
 * Merges the new array to the current list.<br />
 * Note that to the contrary of the <code>Array.concat()</code> method, this does not create a new list, but modifies the current one in place.<br />
 * The internal list may be converted to an Array if needed.
 * @param {any[]} arr The other array.
 * @return {int} The new length of the list.
 */
DuList.prototype.merge = function(arr) {
    this.convertToArray();

    this.list = this.list.concat(arr);

    return this.list.length;
}

/**
 * Merges the new array to the current list.<br />
 * Will only add items if they're not already in the list.<br />
 * Note that to the contrary of the <code>Array.concat()</code> method, this does not create a new list, but modifies the current one in place.<br />
 * The internal list may be converted to an Array if needed.
 * @param {any[]|DuList} arr The other array/list.
 * @param {function} [comparisonFunction] A function which compares two values which returns true if the values are considered the same.
 * @return {int} The new length of the list.
 */
DuList.prototype.mergeUnique = function(arr, comparisonFunction) {
    arr = new DuList(arr);
    this.convertToArray();

    for (var i = 0, n = arr.length(); i < n; i++) {
        this.pushUnique(comparisonFunction, arr.at(i));
    }

    return this.list.length;
}

/**
 * Reimplementation of the <code>Array.join</code> function.<br />
 * For collections, the name of the item will be used, or any other property which makes sense as a string list.
 * @param {string} separator The separator to use
 * @return {string} The new length of the list.
 */
DuList.prototype.join = function(separator) {
    if (this.isArray) return this.list.join(separator);

    // Collection without "name" property
    if (this.list instanceof RQItemCollection) {
        var str = '';
        this.do(function(item) {
            if (!this.current == 0) str += separator;
            str += item.comp.name;
            if (item.status == RQItemStatus.WILL_CONTINUE) str += ' (will continue)';
            else if (item.status == RQItemStatus.NEEDS_OUTPUT) str += ' (no output)';
            else if (item.status == RQItemStatus.UNQUEUED) str += ' (disabled)';
            else if (item.status == RQItemStatus.QUEUED) str += ' (ready)';
            else if (item.status == RQItemStatus.RENDERING) str += ' (rendering)';
            else if (item.status == RQItemStatus.USER_STOPPED) str += ' (stopped)';
            else if (item.status == RQItemStatus.ERR_STOPPED) str += ' (rendering error)';
            else if (item.status == RQItemStatus.DONE) str += ' (done)';
        });
        return str;
    }

    // Other collections have a "name" property
    var str = '';
    this.do(function(item) {
        if (!this.current == 0) str += separator;
        str += item.name;
    });
    return str;
}

/**
 * Replaces all occurences of a value with another value.<br />
 * The internal list may be converted to an Array if needed.
 * @param {any} what The current value
 * @param {any} with The new value
 * @param {function} [comparisonFunction] A function which compares two values which returns true if the values are considered the same.
 * @returns {int} The number of occurences which were updated.
 */
DuList.prototype.replace = function(previousValue, newValue, comparisonFunction) {
    this.convertToArray();

    var useFunction = isdef( comparisonFunction );

    var count = 0;

    for (var i = 0, n = this.length(); i < n; i++) {
        if (!useFunction && this.at(i) == previousValue) {
            this.list[i] = newValue;
            count++;
        } else if (useFunction && comparisonFunction(this.at(i), previousValue)) {
            this.list[i] = newValue;
            count++;
        }
    }

    return count;
}

/**
 * Removes the last element of the list and returns it.<br />
 * The internal list may be converted to an Array if needed.
 * @return {any|null} The last element or null if the list is empty.
 */
DuList.prototype.pop = function() {
    this.convertToArray();
    return this.list.pop();
}

// ================ ITERATOR METHODS ================

/**
 * Reinits the iterator, i.e. go to index -1 so that the next call to {@link DuList.next} returns the first item.
 */
DuList.prototype.reinitIterator = function() {
    this.goTo(-1);
}

/**
 * Sets the iterator on the index
 * @param {int} index - The index
 * @return {*} The item at index, or null if the index is invalid
 */
DuList.prototype.goTo = function(index) {
    this.current = index;

    var count = this.length();

    if (this.current < 0 || this.current >= count) this.valid = false;
    else this.valid = true;

    if (this.current == 0) this.atStart = true;
    else this.atStart = false;

    if (this.current == count - 1) this.atEnd = true;
    else this.atEnd = false;

    if (this.valid) return this.at(this.current);
    else return null;
}

/**
 * Goes to the end of the Iterator
 * @return {Object} The item at the end, or null if length is 0
 */
DuList.prototype.goToEnd = function() {
    if (this.isEmpty()) return null;
    return this.goTo(this.length() - 1);
}

/**
 * Goes to the start of the Iterator
 * @return {Object} The item at the start, or null if length is 0
 */
DuList.prototype.goToStart = function() {
    if (this.isEmpty()) return null;
    return this.goTo(0);
}

/**
 * Increments the Iterator<br />
 * Must be called at least once to validate the iterator
 * @return {any} The next item, or null if there isn't
 */
DuList.prototype.next = function() {
    if (this.isEmpty()) return null;

    if (!this.valid) {
        return this.goToStart();
    }

    if (this.current < this.length() - 1) {
        return this.goTo(this.current + 1);
    }

    this.goTo(this.current + 1);
    return null;
}

/**
 * Decrements the Iterator
 * if it's called while valid is false, goes to the end
 * @return {Object} The previous item, or null if there isn't
 */
DuList.prototype.previous = function() {
    if (this.isEmpty()) return null;

    if (!this.valid) {
        return this.goToEnd();
    }

    if (this.current > 0) {
        return this.goTo(this.current - 1);
    }

    return null;
}

/**
 * Executes a function on each item of the List
 * @param {function} callBack - The function to execute, which takes one parameter: an item of the list
 * @param {Bool} [reverse=false] - Set this to true to iterate from the end.
 */
DuList.prototype.do = function(callBack, reverse) {
    if (this.isEmpty()) return;

    reverse = def(reverse, false);

    // Keep the current index
    var current = this.current;

    if (reverse)
    {
        for (var i = this.length() - 1; i >= 0; i--) {
            this.valid = true;
            this.current = i;
            callBack(this.at(i));
        }
    }
    else
    {
        for (var i = 0, n = this.length(); i < n; i++) {
            this.valid = true;
            this.current = i;
            callBack(this.at(i));
        }
    }
    
    // Restore the current index
    this.goTo(current);
}


// ================ STATIC ==========================

/**
 * Checks if this is an Array or an After Effects collection type or a {@link DuList}
 * @static
 * @param {Array|Collection} list - The list to check
 * @return {boolean} true if this is a Cllection or an Array or a {@link DuList}
 */
DuList.isList = function(list) {
    return (
        list instanceof Array ||
        list instanceof ItemCollection ||
        list instanceof LayerCollection ||
        list instanceof OMCollection ||
        list instanceof RQItemCollection ||
        list instanceof DuList
    );
}

// ================ DEPRECATED ======================


/**
 * A function to compare two elements in an array
 * @callback DuList.indexOf~comparisonFunction
 * @memberof DuList.indexOf
 * @param {*} elementInArray The element in the array
 * @param {*} value The value to compare
 * @return {bool} true if the two values should be considered the same
 */

/**
 * Gets the first index of a value in an Array, or -1 if not found
 * @memberof DuList
 * @static
 * @deprecated
 * @param {Array}	arr	- The array
 * @param {*}	value	- The value to find. Must be compatible with the == operand, or provide a comparison function
 * @param {function}	[comparisonFunction]	- A function which compares two values which returns true if the values are considered the same.
 * @return {int}	The index of the value, -1 if not found
 */
/*DuList.indexOf = function(arr, value, comparisonFunction) {
    if (value == undefined) throw "Value can not be undefined";
    var useFunction = typeof comparisonFunction === 'function';

    for (var i = 0; i < arr.length; i++) {
        if (!useFunction && arr[i] == value) return i;
        if (useFunction && comparisonFunction(arr[i], value)) return i;
    }
    return -1;
}//*/

/**
 * Checks if the array has duplicate values
 * @memberof DuList
 * @static
 * @deprecated
 * @param {Array}	arr	- The array
 * @return {boolean}	true if the array has duplicate values
 */
/*DuList.hasDuplicates = function(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
        for (var j = i + 1; j < arr.length; j++) {
            if (arr[i] === arr[j]) return true;
        }
    }
    return false;
}//*/

/**
 * Returns all duplicated values found in the array
 * @memberof DuList
 * @static
 * @deprecated
 * @param {Array}	arr	- The array
 * @return {Array}	The duplicated values
 */
/*DuList.getDuplicates = function(arr) {
    var duplicates = [];
    for (var i = 0; i < arr.length - 1; i++) {
        for (var j = i + 1; j < arr.length; j++) {
            if (arr[i] === arr[j]) duplicates.push(arr[j]);
        }
    }
    DuList.removeDuplicates(duplicates);
    return duplicates;
}//*/

/**
 * Removes all duplicated values from the Array, and returns them
 * @memberof DuList
 * @static
 * @deprecated
 * @param {Array}	arr	- The array
 * @param {function}	[comparisonFunction] - A function which compares two values which returns true if the values are considered the same.
 * @return {Array}	The duplicated (and removed) values
 */
/*DuList.removeDuplicates = function(arr, comparisonFunction) {
    var useFunction = typeof comparisonFunction === 'function';
    var removed = [];
    for (var i = 0; i < arr.length - 1; i++) {
        for (var j = i + 1; j < arr.length; j++) {
            if (!useFunction && arr[i] === arr[j]) removed = removed.concat(arr.splice(j, 1));
            if (useFunction && comparisonFunction(arr[i], arr[j])) removed = removed.concat(arr.splice(j, 1));
        }
    }
    //sometimes the two last items may still be the same (for a reason I don't understand)
    if (!useFunction && arr[arr.length - 2] === arr[arr.length - 1]) removed.push(arr.pop());
    if (useFunction && comparisonFunction(arr.length - 2, arr.length - 1)) removed.push(arr.pop());

    return removed;
}//*/

/**
 * Compares two arrays.<br />
 * The items in the arrays must be compatible with the == operand
 * @memberof DuList
 * @static
 * @deprecated
 * @param {Array|Collection} array1 - The array
 * @param {Array|Collection} array2 - The array
 * @param {int} [floatPrecision=-1] - The precision for (float) number comparison, number of decimals. Set to -1 to not use.
 * @return {boolean} true if the two arrays contain the same values
 */
/*DuList.compare = function(array1, array2, floatPrecision) {
    if (!DuList.isList(array1) && !DuList.isList(array2)) {
        if (typeof array1 == 'number' && typeof array2 == 'number') {
            return DuMath.equals(array1, array2, floatPrecision)
        } else return array1 == array2;
    }
    if (DuList.isList(array1) && !DuList.isList(array2)) return false;
    if (!DuList.isList(array1) && DuList.isList(array2)) return false;
    if (array1.length != array2.length) return false;

    var it = new DuList(array1);
    var ok = true;
    it.do(function(item1) {
        var item2 = array2[it.current];
        if (!DuList.compare(item1, item2, floatPrecision)) {
            ok = false;
            return;
        }
    });

    return ok;
}
//*/// ==================== |-------| ====================
// ==================== | color | ====================
// ==================== |-------| ====================

/**
 * Constructs a new color object
 * @class
 * @name DuColor
 * @classdesc A simple class to manage colors and convert them.
 * @param {float[]} [floatRGBA=[0,0,0,1]] An [R,G,B,A] float Array.<br />
 * Negative values are clamped to 0.<br />
 * Alpha > 1 is clamped to 1.<br />
 * Colors are stored in 32 bit float to keep the maximum precision.
 * @property {float} red The red value
 * @property {float} green The green value
 * @property {float} blue The blue value
 * @property {float} alpha The alpha value
 * @category DuESF
 */
function DuColor( floatRGBA )
{
	floatRGBA = def(floatRGBA, [0,0,0,0]);
	
	this.red = floatRGBA[0];
	if (this.red < 0) this.red = 0;
	if (!jstype( (this.red ) == 'number') ) this.red = 0;
	if ( isNaN(this.red) ) this.red = 0;

	this.green = floatRGBA[1];
	if (this.green < 0) this.green = 0;
	if (!jstype( (this.green ) == 'number') ) this.green = 0;
	if ( isNaN(this.green) ) this.green = 0;

	this.blue = floatRGBA[2];
	if (this.blue < 0) this.blue = 0;
	if (!jstype( (this.blue ) == 'number') ) this.blue = 0;
	if ( isNaN(this.blue) ) this.blue = 0;

	if (floatRGBA.length > 3) this.alpha = floatRGBA[3];
	else this.alpha = 1.0;
	if (this.alpha < 0) this.alpha = 0;
	if (this.alpha > 1) this.alpha = 1;
	if (!jstype( (this.alpha ) == 'number') ) this.alpha = 0;
	if ( isNaN(this.alpha) ) this.alpha = 0;
}

/**
 * Returns the color as a float Array with alpha
 * @param {bool} [clamped=true] Set to false to keep the values > 1.0
 * @return {float} an [R,G,B,A] Array.
 */
DuColor.prototype.floatRGBA = function(clamped)
{
	clamped = def( clamped, true );

	var col = this.floatRGB();

	var alpha = this.alpha;
	if (clamped) alpha = DuMath.clamp( alpha );

	col.push(alpha);

	return col;
}

/**
 * Returns the color as a float Array without alpha
 * @param {bool} [clamped=true] Set to false to keep the values > 1.0
 * @return {float} an [R,G,B] Array.
 */
DuColor.prototype.floatRGB = function(clamped)
{
	clamped = def( clamped, true );

	var col = [
		this.red,
		this.green,
		this.blue
	]

	if (clamped) return DuMath.clamp( col );

	return col;
}

/**
 * Returns the color as an 8-bit int Array with alpha
 * @return {int} an [R,G,B,A] Array.
 */
DuColor.prototype.intRGBA = function()
{
	var col = this.intRGB();
	col.push( parseInt( this.alpha * 255 ) );
	return col;
}

/**
 * Returns the color as an 8-bit int Array without alpha
 * @return {int} an [R,G,B] Array.
 */
DuColor.prototype.intRGB = function()
{
	return [
		parseInt( this.red * 255 ),
		parseInt( this.green * 255 ),
		parseInt( this.blue * 255 )
	]
}

/**
 * true if [R,G,B,A] in the range 0.0 ... 1.0
 * @return {boolean} true if the color is valid
 */
DuColor.prototype.isValid = function()
{
	if (this.red > 1) return false;
	if (this.green > 1) return false;
	if (this.blue > 1) return false;
	return true;
}

/**
 * Returns the hexcode for this color
 * @return {string} The hex code, without the leading '#'.
 */
DuColor.prototype.hex = function()
{
	var red = this.red*255;
	var green = this.green*255;
	var blue = this.blue*255;
	var hexR = red.toString(16);
	var hexG = green.toString(16);
	var hexB = blue.toString(16);
	if (hexR.length == 1) hexR = "0" + hexR;
	if (hexG.length == 1) hexG = "0" + hexG;
	if (hexB.length == 1) hexB = "0" + hexB;
	
	return hexR+hexG+hexB;
}

/**
 * Returns the hexcode for this color, including the alpha (at the end)
 * @return {string} The hex code, without the leading '#'.
 */
DuColor.prototype.hexA = function()
{
	var alpha = this.alpha*255;
	var hexA = alpha.toString(16);
	if (hexA.length == 1) hexA = "0" + hexA;

	return this.hex() + hexA;
}

/**
 * Returns the HSL values
 * @return {float[]} the HSL
 */
DuColor.prototype.floatHSL = function()
{
	var r = this.red;
	var g = this.blue;
	var b = this.green;

    var max = Math.max(r, g, b), min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;

    if(max == min)
	{
        h = s = 0; // achromatic
    }
	else
	{
        var d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        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, l];
}

/**
 * Returns the HSLA values
 * @return {float[]} the HSL
 */
DuColor.prototype.floatHSLA = function()
{
	var hsla = this.floatHSL();
	hsla.push( this.alpha );
    return hsla;
}

/**
 * Creates a new color darker than the current one
 * @param {int} [ratio=200] A percentage: 200 means twice darker, 50 twice lighter
 * @returns {DuColor} The new color
 */
DuColor.prototype.darker = function( ratio )
{
	ratio = def(ratio, 200);
	ratio = ratio / 100;

	return new DuColor([
		this.red/ratio,
		this.green/ratio,
		this.blue/ratio,
		this.alpha,
	]);
}

/**
 * Creates a new color lighter than the current one
 * @param {int} [ratio=200] A percentage: 200 means twice lighter, 50 twice darker
 * @returns {DuColor} The new color
 */
DuColor.prototype.lighter = function( ratio )
{
	ratio = def(ratio, 200);
	ratio = ratio / 100;

	return new DuColor([
		this.red*ratio,
		this.green*ratio,
		this.blue*ratio,
		this.alpha,
	]);
}

/**
 * Compares two colors
 * @param {Boolean} [ignoreAlpha=false] Set to true to consider colors to be equal if they differ only by their alpha.
 * @param {int} [precision=4] The precision to use (the number of decimals to compare).
 * @return {Boolean} true if the colors are the same.
 */
DuColor.prototype.equals = function( other, ignoreAlpha, precision )
{
	if (!DuMath.equals( this.red, other.red, precision )) return false;
	if (!DuMath.equals( this.green, other.green, precision )) return false;
	if (!DuMath.equals( this.blue, other.blue, precision )) return false;
	if (!DuMath.equals( this.alpha, other.alpha, precision )) return false;
	return true;
}

/**
	* Creates a JSON string representation of the color; Actually an RGBA float Array.
	* @return {string} The JSON
	*/
DuColor.prototype.toJSON = function ()
{
	return '[' + this.red + ',' + this.green + ',' + this.blue + ',' + this.alpha + ']';
}

/**
 * Creates a color adjusted according to the brightness setting of the application.<br />
 * For now, works only in After Effects
 * @return The new color
 */
DuColor.prototype.adjusted = function( )
{
	if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS)
	{
		//gets the main application color
		var q = app.themeColor( 78 )[ 0 ];
		//the darkest one is 0.15000000596046
		q = q - 0.15000000596046;
		var newColor = this.floatRGBA() + [ q, q, q, 0 ];
		return new DuColor( newColor );
	}
    return this;
}

// ============== STATIC ===============

/**
	* Enum for predefined colors. float [R,G,B,A]
	* @readonly
	* @static
	* @enum {DuColor[]}
	*/
DuColor.Color =
{
	TRANSPARENT: new DuColor([0,0,0,0]),
	BLACK: new DuColor([0,0,0,1]),
	WHITE: new DuColor([1,1,1,1]),
	OBSIDIAN: new DuColor([.082,.082,.082,1]),
	ABYSS_GREY: new DuColor([0.109,0.109,0.109,1]),
	VERY_DARK_GREY:new DuColor([0.199,0.199,0.199,1]),
	DARK_GREY: new DuColor([.262,.262,.262,1]),
	LIGHT_GREY: new DuColor([.65,.65,.65,1]),
	VERY_LIGHT_GREY: new DuColor([.85,.85,.85,1]),
	RAINBOX_RED: new DuColor([.925,.094,.094,1]),
	ORANGE: new DuColor([.925,.471,.094,1]),
	YELLOW_ORANGE: new DuColor([.925,.655,.094,1]),
	YELLOW: new DuColor([.925,.839,.094,1]),
	GREEN: new DuColor([.094,.925,.094,1]),
	LIGHT_GREEN: new DuColor([.541,.925,.569,1]),
	LIGHT_BLUE: new DuColor([.471,.847,.925,1]),
	LIGHT_PURPLE: new DuColor([.471,.471,.925,1]),
	AE_DARK_GREY: new DuColor([0.199,0.199,0.199,1]),
	AFTER_EFFECTS_BLUE: new DuColor([.1764,.5490,.9215,1]),
	RX_PURPLE: new DuColor([.7411,.1843,.7686,1]),
	APP_BACKGROUND_COLOR: new DuColor([0.199,0.199,0.199,1]),
	APP_HIGHLIGHT_COLOR: new DuColor([.7411,.1843,.7686,1]),
	APP_TEXT_COLOR: new DuColor( [ 0.6941, 0.6941, 0.6941, 1]),
	AE_ORANGE: new DuColor( [ .7804, .5725, 0, 1])
};

// Get the app colors
DuColor.init = function() 
{
		var highlightSelection = DuESF.scriptSettings.get("common/highlightSelection", -1);
		var highlightColor = new DuColor( DuESF.scriptSettings.get("common/highlightColor", DuColor.Color.APP_HIGHLIGHT_COLOR) );
		DuDebug.log("DuColor: Highlight color selection: " + highlightSelection);

		// Highlight color
		if (highlightSelection > 0)
		{
			if (highlightSelection == 1) DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.RX_PURPLE;
			else if (highlightSelection == 2) DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.RAINBOX_RED;
			else if (highlightSelection == 3) DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.AE_ORANGE;
			else if (highlightSelection > 3) DuColor.Color.APP_HIGHLIGHT_COLOR = highlightColor;

			return;

			DuDebug.log("DuColor: Highlight color is: " + DuColor.Color.APP_HIGHLIGHT_COLOR.hex());
		}

	// ScriptUIGraphics object does not contain foreground & background colors in After Effects...
	if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS) 
	{
		DuColor.Color.APP_TEXT_COLOR = new DuColor( [ 0.6941, 0.6941, 0.6941, 1]);
		var bgColor = app.themeColor(78) - [0.0127, 0.0127, 0.0127];
		bgColor.push(1);
		DuColor.Color.APP_BACKGROUND_COLOR = new DuColor( bgColor );
		DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.AFTER_EFFECTS_BLUE;
		DuColor.Color.DARK_GREY = new DuColor( [.2, .2, .2, 1] );
		DuColor.Color.OBSIDIAN = new DuColor( [.0862, .0862, .0862, 1] );
	}
	else if (DuESF.host == DuESF.HostApplication.PHOTOSHOP)
	{
		DuColor.Color.APP_TEXT_COLOR = new DuColor( [ 0.6941, 0.6941, 0.6941, 1]);
		DuColor.Color.APP_BACKGROUND_COLOR = DuColor.Color.VERY_DARK_GREY;
		DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.DARK_GREY;
		DuColor.Color.DARK_GREY = new DuColor( [.2, .2, .2, 1] );
		DuColor.Color.OBSIDIAN = new DuColor( [.0862, .0862, .0862, 1] );
	}
	else
	{
		// create a label to (try to) get the color
		var dummyWin = new Window( 'palette' );
		var dummyLabel = dummyWin.add('statictext', undefined,'text');

		var textPen = dummyLabel.graphics.foregroundColor;
		var bgBrush = dummyWin.graphics.backgroundColor;

		if (textPen) DuColor.Color.APP_TEXT_COLOR = new DuColor( textPen.color );
		else DuColor.Color.APP_TEXT_COLOR = DuColor.Color.LIGHT_GREY;
		if (bgBrush) DuColor.Color.APP_BACKGROUND_COLOR = new DuColor( bgBrush.color );
		else DuColor.Color.APP_BACKGROUND_COLOR = DuColor.Color.DARK_GREY;
		
		DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.RX_PURPLE;
	}
}

/**
	* Creates a new DuColor from a hex code/array
	* @static
	* @memberof DuColor
	* @param {string|int[]} hexColor	- The hexadecimal color
	* @return {DuColor}	The color
*/
DuColor.fromHex = function( hexColor )
{
	var isString = jstype( hexColor ) == 'string';

	if (isString)
	{
		if (hexColor.indexOf("#") == 0) hexColor = hexColor.replace("#","");
		var red = parseInt(hexColor.substr(0,2),16)/255.0;
		var green = parseInt(hexColor.substr(2,2),16)/255.0;
		var blue = parseInt(hexColor.substr(4,2),16)/255.0;
		return new DuColor( [red,green,blue,1] );
	}
	else
	{
		var r = hexColor >> 16;
		var g = (hexColor & 0x00ff00) >> 8;
		var b = hexColor & 0xff;
		return new DuColor( [r/255,g/255,b/255,1] );
	}
}

/**
 * Creates a new color from HSL(A) values
 * @static
 * @param {float[]} hsl the HSL(A) values
 * @return {DuColor} the color.
 */
DuColor.fromHSL = function( hsl )
{
	var r, g, b, a;
	var h = hsl[0];
	var s = hsl[1];
	var l = hsl[2];

    if(s == 0)
	{
        r = g = b = l; // achromatic
    }
	else
	{
        function hue2rgb(p, q, t)
		{
            if(t < 0) t += 1;
            if(t > 1) t -= 1;
            if(t < 1/6) return p + (q - p) * 6 * t;
            if(t < 1/2) return q;
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
            return p;
        }

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
    }

	if (hsl.length > 3) a = hsl[3];
	else a = 1;

    return new DuColor( [r, g, b, a] );
}

/**
 * Creates a new color from an 8-bit int RGB(A) array
 * @static
 * @param {int[]} source The 8-bit int RGB(A) array
 * @return {DuColor}
 */
DuColor.fromInt = function( intRGB )
{
	var floatColor = [];
	for (var i = 0, numChannels = color.length; i < numChannels; i++)
	{
		floatColor.push( color[ i ] / 255.0 );
	}

	return new DuColor( floatColor );
}

/**
	* Creates a color from a JSON string; Actually a JSON RGBA float Array.
	* @static
	* @param {string} json The JSON string
	* @return {DuColor} The color
	*/
DuColor.fromJSON = function (json)
{
	return new DuColor( JSON.parse(json) );
}

/**
 * Generates a random color, with the alpha == 1.0
 * @static
 * @return {DuColor} The color
 */
DuColor.random = function()
{
	var color = [0,0,0,1];
	for (var i = 0 ; i < 3 ; i++)
	{
		color[i] = DuMath.random();
	}

	return new DuColor(color);
}

// ========= DEPRECATED ==============

/**
 * Checks if this color is valid ([R,G,B,A] in the range 0.0 ... 1.0)
 * @static
 * @deprecated
 * @param {float[]} color - The floatRGBA color to validate
 * @param {boolean} [ignoreAlpha=false] - Will return true even if the array does not have any alpha value
 * @return {boolean} true if the color is valid
 */
DuColor.isValid = function (color, ignoreAlpha)
{
	ignoreAlpha = def( ignoreAlpha, false, false );
	if ( !jstype(color) == 'array' ) return false;
	var numChannels = color.length;
	if ( numChannels != 4 && !ignoreAlpha ) return false;
	if ( numChannels != 4 && numChannels != 3 ) return false;
	for (var i = 0, numChannels; i < numChannels; i++)
	{
		var c = color[i];
		if (isNaN(c)) return false;
		if (!jstype(color) == 'number') return false;
		if (c < 0) color[i] = 0;
		if (c > 1) color[i] = 1;
	}
	return true;
}

/**
	* Generates a random color
	* @static
	* @deprecated
	* @memberof DuColor
	* @return {float[]}	The color as an [R,G,B,A] Array with float values between 0.0 and 1.0
*/
DuColor.randomFloatRGBA = function ()
{
	var color = [0,0,0,1];
	for (var i = 0 ; i < 3 ; i++)
	{
		color[i] = DuMath.random();
	}
	return color;
}

/**
	* Converts an hexadecimal color to a floatRGBA Array
	* @static
	* @deprecated
	* @memberof DuColor
	* @param {string|int[]} hexColor	- The hexadecimal color
	* @param {boolean}	[isString=true] 	- Whether hexColor is a string or an Array of int of base 16
	* @return {float[]}	The color as an [R,G,B,A] Array with float values between 0.0 and 1.0
*/
DuColor.hexToRGB = function (hexColor,isString)
{
	if (isString == undefined) isString = true;
	if (isString)
	{
		if (hexColor.indexOf("#") == 0) hexColor = hexColor.replace("#","");
		var red = parseInt(hexColor.substr(0,2),16)/255.0;
		var green = parseInt(hexColor.substr(2,2),16)/255.0;
		var blue = parseInt(hexColor.substr(4,2),16)/255.0;
		return [red,green,blue,1];
	}
	else
	{
		var r = hexColor >> 16;
		var g = (hexColor & 0x00ff00) >> 8;
		var b = hexColor & 0xff;
		return [r/255,g/255,b/255,1];
	}
}

/**
	* Converts an RGB color to a hex string
	* @memberof DuColor
	* @deprecated
	* @static
	* @param {float[]} rgbColor	- The rgb color
	* @return {string}	The color as an hex string
*/
DuColor.rgbToHex = function (rgbColor)
{
	var red = rgbColor[0]*255;
	var green = rgbColor[1]*255;
	var blue = rgbColor[2]*255;
	var hexR = red.toString(16)
	var hexG = green.toString(16)
	var hexB = blue.toString(16);
	if (hexR.length == 1) hexR = "0" + hexR;
	if (hexG.length == 1) hexG = "0" + hexG;
	if (hexB.length == 1) hexB = "0" + hexB;
	var hex = hexR+hexG+hexB;
	return hex;
}

/**
	* Converts an HSL color to RGB
	* @deprecated
	* @static
	* @param {float[]} color - The RGBA color array
	* @return {float[]} the HSLA color array
*/
DuColor.hslToRgb = function (color)
{
    var r, g, b;
	var h = color[0];
	var s = color[1];
	var l = color[2];

    if(s == 0)
	{
        r = g = b = l; // achromatic
    }
	else
	{
        function hue2rgb(p, q, t)
		{
            if(t < 0) t += 1;
            if(t > 1) t -= 1;
            if(t < 1/6) return p + (q - p) * 6 * t;
            if(t < 1/2) return q;
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
            return p;
        }

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
    }

    return [r, g, b, color[3]];
}

/**
	* Converts an RGB color to HSL
	* @deprecated
	* @static
	* @param {float[]} color - The HSLA color array
	* @return {float[]} the RGBA color array
*/
DuColor.rgbToHsl = function (color)
{
	var r = color[0];
	var g = color[1];
	var b = color[2];

    var max = Math.max(r, g, b), min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2;

    if(max == min)
	{
        h = s = 0; // achromatic
    }
	else
	{
        var d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        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, l, color[3]];
}

/**
 * Converts an 8bpc color array to a 32bpc float color array
 * @deprecated
 * @static
 * @param {int[]} color - The RGB(A) color array in 8bpc (0-255 range)
 * @return {float[]} the RGBA color array
 */
DuColor.eightBpcToFloat = function (color)
{
	var floatColor = [];
	for (var i = 0, numChannels = color.length; i < numChannels; i++)
	{
		floatColor.push( color[ i ] / 255 );
	}
	return floatColor;
}
// ==================== |----------| ====================
// ==================== | settings | ====================
// ==================== |----------| ====================

/**
	* Constructs a new DuSettings instance.
	* @class DuSettings
	* @classdesc A class to manage settings.<br />
	 * Warning: DuESF stores the settings in json files and needs the right to write files, but it does not check if it has write access on the files, you should check that first using methods specific to the host application.
	* @param {string}	  [namespace=DuESF.scriptName]     - A namespace to group your settings.
	* @param {File|string}    [file=Folder.myDocuments/namespace/namespace_settings.json]    - The file to store the settings
	* @property {string}	  namespace	- A namespace to group your settings.
	* @property {File}		file	- The file to store the settings
	* @property {Object}	data	- The settings as a js object
	* @category DuESF
	*/
function DuSettings( namespace, file, dontSavePath )
{
	dontSavePath = def(dontSavePath, false);
	namespace = def(namespace, DuESF.scriptName);
	if ( DuDebug.checkVar( namespace, 'namespace', 'string', DuSettings ) !== true ) throw "Sorry, an error has occured in `new DuSettings()`.";

	if (namespace == "") throw "new Dusettings( namespace, file ): error\n\nnamespace can not be an empty string";

	// Get path from duesf settings
	if ( !isdef( file ) )
	{
		var filePath = DuESF.settings.get("scriptSettingsFile");
		if ( filePath ) file = new File( filePath );
	}

	// Generate default path
	if (!isdef( file ))
	{
		if (DuFolder.canWrite(Folder.myDocuments))
			file = new File( Folder.myDocuments.absoluteURI + "/" + namespace + "/" + namespace + "_settings.json" );
		else
			file = new File( Folder.temp.absoluteURI + "/" + namespace + "/" + namespace + "_settings.json" );
	}

	this.namespace = namespace;
	if (file instanceof File) this.file = file;
	else this.file = new File(file);
	this.file.encoding = 'UTF-8';
	this.folder = this.file.parent;
	
	if (!dontSavePath)
	{
		DuESF.settings.set("scriptSettingsFile", this.file.absoluteURI);
		DuESF.settings.save();
	}
	

	this.data = {};
	this.load();
}

/**
 * loads data from the settings file
 * @memberof DuSettings
 */
DuSettings.prototype.load = function()
{
	//load file
	if (this.file.exists)
	{
		this.data = DuFile.parseJSON( this.file );
		if (!this.data) this.data = {};
	}
}

/**
 * Saves data to the file.<br />
 * Warning: DuESF does not check if it has write access on the files, you should check that first using methods specific to the host application.
 * @memberof DuSettings
 * @return {boolean} true if the file has been correctly written
 */
DuSettings.prototype.save = function()
{
	try 
	{
		if ( !this.folder.exists ) this.folder.create();
	}
	catch (e)
	{
		DuDebug.throwError("Can't create folder", 'DuSettings.prototype.save', e);
		return false;
	}
	return DuFile.saveJSON(this.data, this.file);
}

/**
 * Sets the file to be used to save the settings
 * @param {File|string} file The file or path to the file
 */
DuSettings.prototype.setFile = function(file, dontSavePath)
{
	if (file instanceof File) this.file = file;
	else this.file = new File(file);
	if (file.exists) this.load();
	else this.save();
	if (!dontSavePath) {
		DuESF.settings.set("scriptSettingsFile", this.file.absoluteURI);
		DuESF.settings.save();
	}
}

/**
	* Reset the settings to their default values (removes the settings file!)
	*/
DuSettings.prototype.reset = function()
{
	this.file.remove();
	this.data = {};
}

/**
 * Gets a value from the settings. The key can be a path separated by /
 * @property {string} key The setting to get
 * @property {*} [defaultValue=null] The default value if the key is not set in the settings
 * @return {*} The value
 */
DuSettings.prototype.get = function( key, defaultValue )
{
	defaultValue = def(defaultValue, null);
	var keyArray = key.split("/")
	var numKeys = keyArray.length;
	var data = this.data;
	for( var i = 0; i < numKeys -1; i++) {
		data = def( data[keyArray[i]], {});
	}
	return def( data[keyArray[numKeys-1]], defaultValue);
}

/**
 * Sets a value to the settings. The key can be a path separated by /
 * @property {string} key The setting to set
 * @property {*} value The value to set
 */
DuSettings.prototype.set = function( key, value )
{
	var keyArray = key.split("/")
	var numKeys = keyArray.length;
	var data = this.data;
	for( var i = 0; i < numKeys -1; i++) {
		data[keyArray[i]] = def( data[keyArray[i]], {});
		data = data[keyArray[i]]
	}
	data[keyArray[numKeys-1]] = value;
}

// Low-level undocumented function. Called by DuESF.init()
DuSettings.init = function() {
	DuDebug.log("DuSettings: loading settings...");

	DuESF.settings = new DuSettings( 'DuESF', DuFolder.duesfData.absoluteURI + '/duesf_settings.json', true  );
	
	/**
	 * The settings to store the script specific settings.
	 * @type {DuSettings}
	 * @memberof DuESF
	 * @static
	 */
	DuESF.scriptSettings = new DuSettings( DuESF.scriptName );
	
	// Load defaults
	var debug = DuESF.scriptSettings.get("common/debug", false);

	// Debug mode
	DuESF.debug = debug;
	// Create a debug log
    if (DuESF.debug) DuDebug.debugLog = new DuDebugLog();

	DuDebug.log("DuSettings: Settings ready!");
}
// ==================== |------------| ====================
// ==================== | translator | ====================
// ==================== |------------| ====================

/**
 * gettext.jsxinc.<br />
 * by {@link http://duduf.com Duduf} and Guillaume Potier.<br />
 * @example
 * // Encapsulate everything to avoid global variables!
 * (function()
 * {
 * 		#include "../lib/gettext.js"
 *
 * 		i18n = i18n();
 *
 *		i18n.setMessages('messages', 'en', {
 *			'Test failed!': 'Test successfull!',
 *		}, 'nplurals=2; plural=n>1;');
 *
 *		i18n.setLocale('en');
 *
 *		alert(i18n._('Test failed!'));
 * })();
 * @namespace
 * @author Nicolas Dufresne and contributors
 * @copyright 2022 Nicolas Dufresne, RxLaboratory
 * @version 1.0.0
 * @license MIT
 */
var translationEngine = function (options) {
  options = options || {};
  this && (this.__version = '1.1.1');

  // default values that could be overriden in i18n() construct
  var defaults = {
    domain: 'messages',
    locale: 'en',
    plural_func: function (n) {
      return {
        nplurals: 2,
        plural: (n != 1) ? 1 : 0
      };
    },
    ctxt_delimiter: String.fromCharCode(4), // \u0004
    folder: DuESF.scriptSettings.file.parent.absoluteURI + '/',
    prefix: DuESF.scriptName + "_",
    suffix: ".json"
  };

  // handy mixins taken from underscode.js
  var _ = {
    isObject: function (obj) {
      var type = typeof obj;
      return type === 'function' || type === 'object' && !!obj;
    },
    isArray: function (obj) {
      return toString.call(obj) === '[object Array]';
    }
  };

  var
    _plural_funcs = {},
    _locale = options.locale || defaults.locale,
    _domain = options.domain || defaults.domain,
    _dictionary = {},
    _plural_forms = {},
    _ctxt_delimiter = options.ctxt_delimiter || defaults.ctxt_delimiter,
	_folder = options.folder || defaults.folder,
	_prefix = options.prefix || defaults.prefix,
	_suffix = options.suffix || defaults.suffix;

  if (options.messages) {
    _dictionary[_domain] = {};
    _dictionary[_domain][_locale] = options.messages;
  }

  if (options.plural_forms) {
    _plural_forms[_locale] = options.plural_forms;
  }

  // language codes / language names associative array
  // Generated from https://github.com/unicode-org/cldr
  var _languageNames = {"af": "Afrikaans", "agq": "Aghem", "ak": "Akan", "am": "\u12a0\u121b\u122d\u129b", "ar": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "as": "\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be", "asa": "Kipare", "ast": "Asturianu", "az": "Az\u0259rbaycan", "bas": "\u0181\u00e0s\u00e0a", "be": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f", "bem": "Ichibemba", "bez": "Hibena", "bg": "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438", "bm": "Bamanakan", "bn": "\u09ac\u09be\u0982\u09b2\u09be", "bo": "\u0f56\u0f7c\u0f51\u0f0b\u0f66\u0f90\u0f51\u0f0b", "br": "Brezhoneg", "brx": "\u092c\u0930\u2019", "bs": "Bosanski", "ca": "Catal\u00e0", "ccp": "\ud804\udd0c\ud804\udd0b\ud804\udd34\ud804\udd1f\ud804\udd33\ud804\udd26", "ce": "\u041d\u043e\u0445\u0447\u0438\u0439\u043d", "ceb": "Binisaya", "cgg": "Rukiga", "chr": "\u13e3\uab83\uab79", "ckb": "\u06a9\u0648\u0631\u062f\u06cc\u06cc \u0646\u0627\u0648\u06d5\u0646\u062f\u06cc", "cs": "\u010ce\u0161tina", "cy": "Cymraeg", "da": "Dansk", "dav": "Kitaita", "de": "Deutsch", "dje": "Zarmaciine", "doi": "\u0921\u094b\u0917\u0930\u0940", "dsb": "Dolnoserb\u0161\u0107ina", "dua": "Du\u00e1l\u00e1", "dyo": "Joola", "dz": "\u0f62\u0fab\u0f7c\u0f44\u0f0b\u0f41", "ebu": "K\u0129embu", "ee": "E\u028begbe", "el": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac", "en": "English", "eo": "Esperanto", "es": "Espa\u00f1ol", "et": "Eesti", "eu": "Euskara", "ewo": "Ewondo", "fa": "\u0641\u0627\u0631\u0633\u06cc", "ff": "Pulaar", "fi": "Suomi", "fil": "Filipino", "fo": "F\u00f8royskt", "fr": "Fran\u00e7ais", "fur": "Furlan", "fy": "Frysk", "ga": "Gaeilge", "gd": "G\u00e0idhlig", "gl": "Galego", "gsw": "Schwiizert\u00fc\u00fctsch", "gu": "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0", "guz": "Ekegusii", "gv": "Gaelg", "ha": "Hausa", "haw": "\u02bb\u014dlelo hawai\u02bbi", "he": "\u05e2\u05d1\u05e8\u05d9\u05ea", "hi": "\u0939\u093f\u0928\u094d\u0926\u0940", "hr": "Hrvatski", "hsb": "Hornjoserb\u0161\u0107ina", "hu": "Magyar", "hy": "\u0540\u0561\u0575\u0565\u0580\u0565\u0576", "ia": "Interlingua", "id": "Indonesia", "ig": "Igbo", "ii": "\ua188\ua320\ua259", "is": "\u00cdslenska", "it": "Italiano", "ja": "\u65e5\u672c\u8a9e", "jgo": "Nda\ua78ca", "jmc": "Kimachame", "jv": "Jawa", "ka": "\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8", "kab": "Taqbaylit", "kam": "Kikamba", "kde": "Chimakonde", "kea": "Kabuverdianu", "kgp": "Kanhg\u00e1g", "khq": "Koyra ciini", "ki": "Gikuyu", "kk": "\u049a\u0430\u0437\u0430\u049b \u0442\u0456\u043b\u0456", "kkj": "Kak\u0254", "kl": "Kalaallisut", "kln": "Kalenjin", "km": "\u1781\u17d2\u1798\u17c2\u179a", "kn": "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1", "ko": "\ud55c\uad6d\uc5b4", "kok": "\u0915\u094b\u0902\u0915\u0923\u0940", "ks": "\u06a9\u0672\u0634\u064f\u0631", "ksb": "Kishambaa", "ksf": "Rikpa", "ksh": "K\u00f6lsch", "ku": "Kurd\u00ee", "kw": "Kernewek", "ky": "\u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430", "lag": "K\u0268laangi", "lb": "L\u00ebtzebuergesch", "lg": "Luganda", "lkt": "Lak\u021f\u00f3l\u02bciyapi", "ln": "Ling\u00e1la", "lo": "\u0ea5\u0eb2\u0ea7", "lrc": "\u0644\u06ca\u0631\u06cc \u0634\u0648\u0645\u0627\u0644\u06cc", "lt": "Lietuvi\u0173", "lu": "Tshiluba", "luo": "Dholuo", "luy": "Luluhia", "lv": "Latvie\u0161u", "mai": "\u092e\u0948\u0925\u093f\u0932\u0940", "mas": "Maa", "mer": "K\u0129m\u0129r\u0169", "mfe": "Kreol morisien", "mg": "Malagasy", "mgh": "Makua", "mgo": "Meta\u02bc", "mi": "Te reo m\u0101ori", "mk": "\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438", "ml": "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02", "mn": "\u041c\u043e\u043d\u0433\u043e\u043b", "mni": "\u09ae\u09c8\u09a4\u09c8\u09b2\u09cb\u09a8\u09cd", "mr": "\u092e\u0930\u093e\u0920\u0940", "ms": "Melayu", "mt": "Malti", "mua": "Munda\u014b", "my": "\u1019\u103c\u1014\u103a\u1019\u102c", "mzn": "\u0645\u0627\u0632\u0631\u0648\u0646\u06cc", "naq": "Khoekhoegowab", "nd": "Isindebele", "nds": "Neddersass\u2019sch", "ne": "\u0928\u0947\u092a\u093e\u0932\u0940", "nl": "Nederlands", "nmg": "Kwasio", "nnh": "Shw\u00f3\u014b\u00f2 ngiemb\u0254\u0254n", "no": "Norsk", "nus": "Thok nath", "nyn": "Runyankore", "om": "Oromoo", "or": "\u0b13\u0b21\u0b3c\u0b3f\u0b06", "os": "\u0418\u0440\u043e\u043d", "pa": "\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40", "pcm": "Naij\u00edri\u00e1 p\u00edjin", "pl": "Polski", "ps": "\u067e\u069a\u062a\u0648", "pt": "Portugu\u00eas", "qu": "Runasimi", "rm": "Rumantsch", "rn": "Ikirundi", "ro": "Rom\u00e2n\u0103", "rof": "Kihorombo", "ru": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439", "rw": "Kinyarwanda", "rwk": "Kiruwa", "sa": "\u0938\u0902\u0938\u094d\u0915\u0943\u0924 \u092d\u093e\u0937\u093e", "sah": "\u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430", "saq": "Kisampur", "sat": "\u1c65\u1c5f\u1c71\u1c5b\u1c5f\u1c72\u1c64", "sbp": "Ishisangu", "sc": "Sardu", "sd": "\u0633\u0646\u068c\u064a", "se": "Davvis\u00e1megiella", "seh": "Sena", "ses": "Koyraboro senni", "sg": "S\u00e4ng\u00f6", "shi": "\u2d5c\u2d30\u2d5b\u2d4d\u2d43\u2d49\u2d5c", "si": "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd", "sk": "Sloven\u010dina", "sl": "Sloven\u0161\u010dina", "smn": "Anar\u00e2\u0161kiel\u00e2", "sn": "Chishona", "so": "Soomaali", "sq": "Shqip", "sr": "\u0421\u0440\u043f\u0441\u043a\u0438", "su": "Basa sunda", "sv": "Svenska", "sw": "Kiswahili", "ta": "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd", "te": "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41", "teo": "Kiteso", "tg": "\u0422\u043e\u04b7\u0438\u043a\u04e3", "th": "\u0e44\u0e17\u0e22", "ti": "\u1275\u130d\u122d\u129b", "tk": "T\u00fcrkmen dili", "to": "Lea fakatonga", "tr": "T\u00fcrk\u00e7e", "tt": "\u0422\u0430\u0442\u0430\u0440", "twq": "Tasawaq senni", "tzm": "Tamazi\u0263t n la\u1e6dla\u1e63", "ug": "\u0626\u06c7\u064a\u063a\u06c7\u0631\u0686\u06d5", "uk": "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430", "ur": "\u0627\u0631\u062f\u0648", "uz": "O\u2018zbek", "vai": "\ua559\ua524", "vi": "Ti\u1ebfng vi\u1ec7t", "vun": "Kyivunjo", "wae": "Walser", "wo": "Wolof", "xh": "Isixhosa", "xog": "Olusoga", "yav": "Nuasue", "yi": "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9", "yo": "\u00c8d\u00e8 yor\u00f9b\u00e1", "yrl": "Nhe\u1ebdgatu", "yue": "\u4e2d\u6587 (\u7cb5\u8a9e)", "zgh": "\u2d5c\u2d30\u2d4e\u2d30\u2d63\u2d49\u2d56\u2d5c", "zh": "\u666e\u901a\u8bdd", "zu": "Isizulu"};
  var _languageCodes= {"Afrikaans": "af", "Aghem": "agq", "Akan": "ak", "\u12a0\u121b\u122d\u129b": "am", "\u0627\u0644\u0639\u0631\u0628\u064a\u0629": "ar", "\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be": "as", "Kipare": "asa", "Asturianu": "ast", "Az\u0259rbaycan": "az", "\u0181\u00e0s\u00e0a": "bas", "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f": "be", "Ichibemba": "bem", "Hibena": "bez", "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438": "bg", "Bamanakan": "bm", "\u09ac\u09be\u0982\u09b2\u09be": "bn", "\u0f56\u0f7c\u0f51\u0f0b\u0f66\u0f90\u0f51\u0f0b": "bo", "Brezhoneg": "br", "\u092c\u0930\u2019": "brx", "Bosanski": "bs", "Catal\u00e0": "ca", "\ud804\udd0c\ud804\udd0b\ud804\udd34\ud804\udd1f\ud804\udd33\ud804\udd26": "ccp", "\u041d\u043e\u0445\u0447\u0438\u0439\u043d": "ce", "Binisaya": "ceb", "Rukiga": "cgg", "\u13e3\uab83\uab79": "chr", "\u06a9\u0648\u0631\u062f\u06cc\u06cc \u0646\u0627\u0648\u06d5\u0646\u062f\u06cc": "ckb", "\u010ce\u0161tina": "cs", "Cymraeg": "cy", "Dansk": "da", "Kitaita": "dav", "Deutsch": "de", "Zarmaciine": "dje", "\u0921\u094b\u0917\u0930\u0940": "doi", "Dolnoserb\u0161\u0107ina": "dsb", "Du\u00e1l\u00e1": "dua", "Joola": "dyo", "\u0f62\u0fab\u0f7c\u0f44\u0f0b\u0f41": "dz", "K\u0129embu": "ebu", "E\u028begbe": "ee", "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac": "el", "English": "en", "Esperanto": "eo", "Espa\u00f1ol": "es", "Eesti": "et", "Euskara": "eu", "Ewondo": "ewo", "\u0641\u0627\u0631\u0633\u06cc": "fa", "Pulaar": "ff", "Suomi": "fi", "Filipino": "fil", "F\u00f8royskt": "fo", "Fran\u00e7ais": "fr", "Furlan": "fur", "Frysk": "fy", "Gaeilge": "ga", "G\u00e0idhlig": "gd", "Galego": "gl", "Schwiizert\u00fc\u00fctsch": "gsw", "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0": "gu", "Ekegusii": "guz", "Gaelg": "gv", "Hausa": "ha", "\u02bb\u014dlelo hawai\u02bbi": "haw", "\u05e2\u05d1\u05e8\u05d9\u05ea": "he", "\u0939\u093f\u0928\u094d\u0926\u0940": "hi", "Hrvatski": "hr", "Hornjoserb\u0161\u0107ina": "hsb", "Magyar": "hu", "\u0540\u0561\u0575\u0565\u0580\u0565\u0576": "hy", "Interlingua": "ia", "Indonesia": "id", "Igbo": "ig", "\ua188\ua320\ua259": "ii", "\u00cdslenska": "is", "Italiano": "it", "\u65e5\u672c\u8a9e": "ja", "Nda\ua78ca": "jgo", "Kimachame": "jmc", "Jawa": "jv", "\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8": "ka", "Taqbaylit": "kab", "Kikamba": "kam", "Chimakonde": "kde", "Kabuverdianu": "kea", "Kanhg\u00e1g": "kgp", "Koyra ciini": "khq", "Gikuyu": "ki", "\u049a\u0430\u0437\u0430\u049b \u0442\u0456\u043b\u0456": "kk", "Kak\u0254": "kkj", "Kalaallisut": "kl", "Kalenjin": "kln", "\u1781\u17d2\u1798\u17c2\u179a": "km", "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1": "kn", "\ud55c\uad6d\uc5b4": "ko", "\u0915\u094b\u0902\u0915\u0923\u0940": "kok", "\u06a9\u0672\u0634\u064f\u0631": "ks", "Kishambaa": "ksb", "Rikpa": "ksf", "K\u00f6lsch": "ksh", "Kurd\u00ee": "ku", "Kernewek": "kw", "\u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430": "ky", "K\u0268laangi": "lag", "L\u00ebtzebuergesch": "lb", "Luganda": "lg", "Lak\u021f\u00f3l\u02bciyapi": "lkt", "Ling\u00e1la": "ln", "\u0ea5\u0eb2\u0ea7": "lo", "\u0644\u06ca\u0631\u06cc \u0634\u0648\u0645\u0627\u0644\u06cc": "lrc", "Lietuvi\u0173": "lt", "Tshiluba": "lu", "Dholuo": "luo", "Luluhia": "luy", "Latvie\u0161u": "lv", "\u092e\u0948\u0925\u093f\u0932\u0940": "mai", "Maa": "mas", "K\u0129m\u0129r\u0169": "mer", "Kreol morisien": "mfe", "Malagasy": "mg", "Makua": "mgh", "Meta\u02bc": "mgo", "Te reo m\u0101ori": "mi", "\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438": "mk", "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02": "ml", "\u041c\u043e\u043d\u0433\u043e\u043b": "mn", "\u09ae\u09c8\u09a4\u09c8\u09b2\u09cb\u09a8\u09cd": "mni", "\u092e\u0930\u093e\u0920\u0940": "mr", "Melayu": "ms", "Malti": "mt", "Munda\u014b": "mua", "\u1019\u103c\u1014\u103a\u1019\u102c": "my", "\u0645\u0627\u0632\u0631\u0648\u0646\u06cc": "mzn", "Khoekhoegowab": "naq", "Isindebele": "nd", "Neddersass\u2019sch": "nds", "\u0928\u0947\u092a\u093e\u0932\u0940": "ne", "Nederlands": "nl", "Kwasio": "nmg", "Shw\u00f3\u014b\u00f2 ngiemb\u0254\u0254n": "nnh", "Norsk": "no", "Thok nath": "nus", "Runyankore": "nyn", "Oromoo": "om", "\u0b13\u0b21\u0b3c\u0b3f\u0b06": "or", "\u0418\u0440\u043e\u043d": "os", "\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40": "pa", "Naij\u00edri\u00e1 p\u00edjin": "pcm", "Polski": "pl", "\u067e\u069a\u062a\u0648": "ps", "Portugu\u00eas": "pt", "Runasimi": "qu", "Rumantsch": "rm", "Ikirundi": "rn", "Rom\u00e2n\u0103": "ro", "Kihorombo": "rof", "\u0420\u0443\u0441\u0441\u043a\u0438\u0439": "ru", "Kinyarwanda": "rw", "Kiruwa": "rwk", "\u0938\u0902\u0938\u094d\u0915\u0943\u0924 \u092d\u093e\u0937\u093e": "sa", "\u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430": "sah", "Kisampur": "saq", "\u1c65\u1c5f\u1c71\u1c5b\u1c5f\u1c72\u1c64": "sat", "Ishisangu": "sbp", "Sardu": "sc", "\u0633\u0646\u068c\u064a": "sd", "Davvis\u00e1megiella": "se", "Sena": "seh", "Koyraboro senni": "ses", "S\u00e4ng\u00f6": "sg", "\u2d5c\u2d30\u2d5b\u2d4d\u2d43\u2d49\u2d5c": "shi", "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd": "si", "Sloven\u010dina": "sk", "Sloven\u0161\u010dina": "sl", "Anar\u00e2\u0161kiel\u00e2": "smn", "Chishona": "sn", "Soomaali": "so", "Shqip": "sq", "\u0421\u0440\u043f\u0441\u043a\u0438": "sr", "Basa sunda": "su", "Svenska": "sv", "Kiswahili": "sw", "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd": "ta", "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41": "te", "Kiteso": "teo", "\u0422\u043e\u04b7\u0438\u043a\u04e3": "tg", "\u0e44\u0e17\u0e22": "th", "\u1275\u130d\u122d\u129b": "ti", "T\u00fcrkmen dili": "tk", "Lea fakatonga": "to", "T\u00fcrk\u00e7e": "tr", "\u0422\u0430\u0442\u0430\u0440": "tt", "Tasawaq senni": "twq", "Tamazi\u0263t n la\u1e6dla\u1e63": "tzm", "\u0626\u06c7\u064a\u063a\u06c7\u0631\u0686\u06d5": "ug", "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430": "uk", "\u0627\u0631\u062f\u0648": "ur", "O\u2018zbek": "uz", "\ua559\ua524": "vai", "Ti\u1ebfng vi\u1ec7t": "vi", "Kyivunjo": "vun", "Walser": "wae", "Wolof": "wo", "Isixhosa": "xh", "Olusoga": "xog", "Nuasue": "yav", "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9": "yi", "\u00c8d\u00e8 yor\u00f9b\u00e1": "yo", "Nhe\u1ebdgatu": "yrl", "\u7cb5\u8a9e": "yue", "\u4e2d\u6587 (\u7cb5\u8a9e)": "yue", "\u2d5c\u2d30\u2d4e\u2d30\u2d63\u2d49\u2d56\u2d5c": "zgh", "\u4e2d\u6587": "zh", "\u666e\u901a\u8bdd": "zh", "Isizulu": "zu"}

  // sprintf equivalent, takes a string and some arguments to make a computed string
  // eg: strfmt("%1 dogs are in %2", 7, "the kitchen"); => "7 dogs are in the kitchen"
  // eg: strfmt("I like %1, bananas and %1", "apples"); => "I like apples, bananas and apples"
  // NB: removes msg context if there is one present
  var strfmt = function (fmt) {
    var args = arguments;

    return fmt
      // put space after double % to prevent placeholder replacement of such matches
      .replace(/%%/g, '%% ')
      // replace placeholders
      .replace(/%(\d+)/g, function (str, p1) {
        return args[p1];
      })
      // replace double % and space with single %
      .replace(/%% /g, '%')
  };

  var removeContext = function (str) {
    // if there is context, remove it
    if (str.indexOf(_ctxt_delimiter) !== -1) {
      var parts = str.split(_ctxt_delimiter);
      return parts[1];
    }

    return str;
  };

  var normalizeLocale = function (locale) {
    // Convert locale to BCP 47. If the locale is in POSIX format, locale variant and encoding is discarded.
    locale = locale.replace('_', '-');
    var i = locale.search(/[.@]/);
    if (i != -1) locale = locale.slice(0, i);
    return locale;
  };

  var getPluralFunc = function (plural_form) {
    // Plural form string regexp
    // taken from https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
    // plural forms list available here http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
    var pf_re = new RegExp('^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_\(\)])+');

    if (!pf_re.test(plural_form))
      throw new Error(strfmt('The plural form "%1" is not valid', plural_form));

    // Careful here, this is a hidden eval() equivalent..
    // Risk should be reasonable though since we test the plural_form through regex before
    // taken from https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
    // TODO: should test if https://github.com/soney/jsep present and use it if so
    return new Function("n", 'var plural, nplurals; ' + plural_form + ' return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };');
  };

  // Proper translation function that handle plurals and directives
  // Contains juicy parts of https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
  var t = function (messages, n, options /* ,extra */ ) {
    // Singular is very easy, just pass dictionnary message through strfmt
    if (!options.plural_form)
      return strfmt.apply(this, [removeContext(messages[0])].concat(Array.prototype.slice.call(arguments, 3)));

    var plural;

    // if a plural func is given, use that one
    if (options.plural_func) {
      plural = options.plural_func(n);

      // if plural form never interpreted before, do it now and store it
    } else if (!_plural_funcs[_locale]) {
      _plural_funcs[_locale] = getPluralFunc(_plural_forms[_locale]);
      plural = _plural_funcs[_locale](n);

      // we have the plural function, compute the plural result
    } else {
      plural = _plural_funcs[_locale](n);
    }

    // If there is a problem with plurals, fallback to singular one
    if ('undefined' === typeof plural.plural || plural.plural > plural.nplurals || messages.length <= plural.plural)
      plural.plural = 0;

    return strfmt.apply(this, [removeContext(messages[plural.plural]), n].concat(Array.prototype.slice.call(arguments, 3)));
  };

  return {
    strfmt: strfmt, // expose strfmt util

    // Declare shortcuts
    tr: function () {
      return this.gettext.apply(this, arguments);
    },
    _: function () {
      return this.gettext.apply(this, arguments);
    },
    __: function () {
      return this.gettext.apply(this, arguments);
    },
    _n: function () {
      return this.ngettext.apply(this, arguments);
    },
    _p: function () {
      return this.pgettext.apply(this, arguments);
    },

    setMessages: function (domain, locale, messages, plural_forms) {
      if (!domain || !locale || !messages)
        throw new Error('You must provide a domain, a locale and messages');

      if ('string' !== typeof domain || 'string' !== typeof locale || !_.isObject(messages))
        throw new Error('Invalid arguments');

      locale = normalizeLocale(locale);

      if (plural_forms)
        _plural_forms[locale] = plural_forms;

      if (!_dictionary[domain])
        _dictionary[domain] = {};

      DuDebug.log("Loaded " + locale + " dictionnary.");

      _dictionary[domain][locale] = messages;

      return this;
    },
    loadJSON: function (jsonData, domain) {
      if (!_.isObject(jsonData))
        jsonData = JSON.parse(jsonData);

      if (!jsonData[''] || !jsonData['']['language'] || !jsonData['']['plural-forms'])
        throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');

      var headers = jsonData[''];
      delete jsonData[''];

      return this.setMessages(domain || defaults.domain, headers['language'], jsonData, headers['plural-forms']);
    },
    setLanguage: function(lang, loadJson) {
      this.setLocale( _languageCodes[lang], loadJson);
      return this;
    },
    setLocale: function (locale, loadJson) {

      if (locale == "en") {
        DuDebug.log("gettext: Using default (en - English)")
        _locale = "en";
        return;
      }

      DuDebug.log("gettext: Using " + locale);

      if ( def(loadJson, false) ) {
        // Get file and load it
        var jsonFile = DuPath.join([_folder, _prefix + locale + _suffix]);
        if (!File(jsonFile).exists)
        {
          DuDebug.log("gettext: Can't find translation file: " + jsonFile)
          return;
        }
        var jsonData = DuFile.parseJSON(jsonFile);
        this.loadJSON(jsonData);
      }

      _locale = normalizeLocale(locale);

      DuDebug.log("gettext: Ready! " + this.getLanguage());

      return this;
    },
    getLanguage: function() {
      return _languageNames[_locale];
    },
    getLanguageForLocale: function (locale) {
      return _languageNames[locale];
    },
    getLocale: function () {
      return _locale;
    },
    getLocaleForLanguage: function (language) {
      return _languageCodes[language];
    },
    getAvailableLanguages: function( defaultLocale ) {
      defaultLocale = def(defaultLocale, "en");

      folder = new Folder(_folder);
      files = folder.getFiles( _prefix + "*" + _suffix);
      var languages = {};
      languages.count = 0;
      languages[defaultLocale] = _languageNames[defaultLocale];
      for (var i = 0; i < files.length; i++)
      {
        var locale = files[i].name.replace(_prefix, "").replace(_suffix, "");
        if (locale.length <= 3)
        {
          languages[locale] = _languageNames[locale];
          languages.count++;
        }
      }
      return languages;
    },
    // getter/setter for domain
    textdomain: function (domain) {
      if (!domain)
        return _domain;
      _domain = domain;
      return this;
    },
    gettext: function (msgid /* , extra */ ) {
      return this.dcnpgettext.apply(this, [undefined, undefined, msgid, undefined, undefined].concat(Array.prototype.slice.call(arguments, 1)));
    },
    ngettext: function (msgid, msgid_plural, n /* , extra */ ) {
      return this.dcnpgettext.apply(this, [undefined, undefined, msgid, msgid_plural, n].concat(Array.prototype.slice.call(arguments, 3)));
    },
    pgettext: function (msgctxt, msgid /* , extra */ ) {
      return this.dcnpgettext.apply(this, [undefined, msgctxt, msgid, undefined, undefined].concat(Array.prototype.slice.call(arguments, 2)));
    },
    dcnpgettext: function (domain, msgctxt, msgid, msgid_plural, n /* , extra */ ) {
      domain = domain || _domain;

      if ('string' !== typeof msgid)
        throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string', msgid));

      var
        translation,
        options = {
          plural_form: false
        },
        key = msgctxt ? msgctxt + _ctxt_delimiter + msgid : msgid,
        exist,
        locale;

      locale = _locale;

      if (locale != "en") exist = _dictionary[domain];
      else exist = false;

      // Find corresponding locale in dict
      // Ignore en to improve perf
      if (exist && locale != "en") {
        for (j in _dictionary[domain]) {
          
          if (locale == j) break;

          // If first part of the locale is ours, use it
          if (j.indexOf(locale) == 0) {
            locale = j;
            break;
          }
        }
      }

      exist = exist && _dictionary[domain][locale] && _dictionary[domain][locale][key];

      // because it's not possible to define both a singular and a plural form of the same msgid,
      // we need to check that the stored form is the same as the expected one.
      // if not, we'll just ignore the translation and consider it as not translated.
      if (msgid_plural) {
        exist = exist && "string" !== typeof _dictionary[domain][locale][key];
      } else {
        exist = exist && "string" === typeof _dictionary[domain][locale][key];
      }

      if (!exist) {
        translation = msgid;
        options.plural_func = defaults.plural_func;
      } else {
        translation = _dictionary[domain][locale][key];
      }

      // Singular form
      if (!msgid_plural)
        return t.apply(this, [
          [translation], n, options
        ].concat(Array.prototype.slice.call(arguments, 5)));

      // Plural one
      options.plural_form = true;
      return t.apply(this, [exist ? translation : [msgid, msgid_plural], n, options].concat(Array.prototype.slice.call(arguments, 5)));
    }
  };
};

// Init the engine
/**
 * The translation engine
 */
var i18n = {};

translationEngine.init = function() {

  var trFolder = DuESF.scriptSettings.file.parent.absoluteURI + '/';

  i18n = translationEngine({
    folder: trFolder,
    prefix: DuESF.scriptName + "_"
    });
  
  var currentLocale = DuESF.scriptSettings.get("common/language", 'eo');
  i18n.setLocale(currentLocale, true);
  
}// ==================== |------| ====================
// ==================== | JSON | ====================
// ==================== |------| ====================

/**
 * JSON parser.
 * @name JSON
 * @namespace
 * @see {@link http://www.JSON.org/js.html|Json2}
 * @license Public-Domain
 * @category DuESF
 * @subcategory Third Party
 */

JSON = {};

 (function () {
     "use strict";

     var rx_one = /^[\],:{}\s]*$/;
     var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
     var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
     var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
     var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
     var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

     function f(n) {
         // Format integers to have at least two digits.
         return (n < 10)
             ? "0" + n
             : n;
     }

     function this_value() {
         return this.valueOf();
     }

     if (typeof Date.prototype.toJSON !== "function") {

         Date.prototype.toJSON = function () {

             return isFinite(this.valueOf())
                 ? (
                     this.getUTCFullYear()
                     + "-"
                     + f(this.getUTCMonth() + 1)
                     + "-"
                     + f(this.getUTCDate())
                     + "T"
                     + f(this.getUTCHours())
                     + ":"
                     + f(this.getUTCMinutes())
                     + ":"
                     + f(this.getUTCSeconds())
                     + "Z"
                 )
                 : null;
         };

         Boolean.prototype.toJSON = this_value;
         Number.prototype.toJSON = this_value;
         String.prototype.toJSON = this_value;
     }

     var gap;
     var indent;
     var meta;
     var rep;


     function quote(string) {

 // If the string contains no control characters, no quote characters, and no
 // backslash characters, then we can safely slap some quotes around it.
 // Otherwise we must also replace the offending characters with safe escape
 // sequences.

         rx_escapable.lastIndex = 0;
         return rx_escapable.test(string)
             ? "\"" + string.replace(rx_escapable, function (a) {
                 var c = meta[a];
                 return typeof c === "string"
                     ? c
                     : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
             }) + "\""
             : "\"" + string + "\"";
     }


     function str(key, holder) {

 // Produce a string from holder[key].

         var i;          // The loop counter.
         var k;          // The member key.
         var v;          // The member value.
         var length;
         var mind = gap;
         var partial;
         var value = holder[key];

 // If the value has a toJSON method, call it to obtain a replacement value.

         if (
             value
             && typeof value === "object"
             && typeof value.toJSON === "function"
         ) {
             value = value.toJSON(key);
         }

 // If we were called with a replacer function, then call the replacer to
 // obtain a replacement value.

         if (typeof rep === "function") {
             value = rep.call(holder, key, value);
         }

 // What happens next depends on the value's type.

         switch (typeof value) {
         case "string":
             return quote(value);

         case "number":

 // JSON numbers must be finite. Encode non-finite numbers as null.

             return (isFinite(value))
                 ? String(value)
                 : "null";

         case "boolean":
         case "null":

 // If the value is a boolean or null, convert it to a string. Note:
 // typeof null does not produce "null". The case is included here in
 // the remote chance that this gets fixed someday.

             return String(value);

 // If the type is "object", we might be dealing with an object or an array or
 // null.

         case "object":

 // Due to a specification blunder in ECMAScript, typeof null is "object",
 // so watch out for that case.

             if (!value) {
                 return "null";
             }

 // Make an array to hold the partial results of stringifying this object value.

             gap += indent;
             partial = [];

 // Is the value an array?

             if (Object.prototype.toString.apply(value) === "[object Array]") {

 // The value is an array. Stringify every element. Use null as a placeholder
 // for non-JSON values.

                 length = value.length;
                 for (i = 0; i < length; i += 1) {
                     partial[i] = str(i, value) || "null";
                 }

 // Join all of the elements together, separated with commas, and wrap them in
 // brackets.

                 v = partial.length === 0
                     ? "[]"
                     : gap
                         ? (
                             "[\n"
                             + gap
                             + partial.join(",\n" + gap)
                             + "\n"
                             + mind
                             + "]"
                         )
                         : "[" + partial.join(",") + "]";
                 gap = mind;
                 return v;
             }

 // If the replacer is an array, use it to select the members to be stringified.

             if (rep && typeof rep === "object") {
                 length = rep.length;
                 for (i = 0; i < length; i += 1) {
                     if (typeof rep[i] === "string") {
                         k = rep[i];
                         v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (
                                 (gap)
                                     ? ": "
                                     : ":"
                             ) + v);
                         }
                     }
                 }
             } else {

 // Otherwise, iterate through all of the keys in the object.

                 for (k in value) {
                     if (Object.prototype.hasOwnProperty.call(value, k)) {
                         v = str(k, value);
                         if (v) {
                             partial.push(quote(k) + (
                                 (gap)
                                     ? ": "
                                     : ":"
                             ) + v);
                         }
                     }
                 }
             }

 // Join all of the member texts together, separated with commas,
 // and wrap them in braces.

             v = partial.length === 0
                 ? "{}"
                 : gap
                     ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
                     : "{" + partial.join(",") + "}";
             gap = mind;
             return v;
         }
     }

 // If the JSON object does not yet have a stringify method, give it one.

     if (typeof JSON.stringify !== "function") {
         meta = {    // table of character substitutions
             "\b": "\\b",
             "\t": "\\t",
             "\n": "\\n",
             "\f": "\\f",
             "\r": "\\r",
             "\"": "\\\"",
             "\\": "\\\\"
         };
         JSON.stringify = function (value, replacer, space) {

 // The stringify method takes a value and an optional replacer, and an optional
 // space parameter, and returns a JSON text. The replacer can be a function
 // that can replace values, or an array of strings that will select the keys.
 // A default replacer method can be provided. Use of the space parameter can
 // produce text that is more easily readable.

             var i;
             gap = "";
             indent = "";

 // If the space parameter is a number, make an indent string containing that
 // many spaces.

             if (typeof space === "number") {
                 for (i = 0; i < space; i += 1) {
                     indent += " ";
                 }

 // If the space parameter is a string, it will be used as the indent string.

             } else if (typeof space === "string") {
                 indent = space;
             }

 // If there is a replacer, it must be a function or an array.
 // Otherwise, throw an error.

             rep = replacer;
             if (replacer && typeof replacer !== "function" && (
                 typeof replacer !== "object"
                 || typeof replacer.length !== "number"
             )) {
                 throw new Error("JSON.stringify");
             }

 // Make a fake root object containing our value under the key of "".
 // Return the result of stringifying the value.

             return str("", {"": value});
         };
     }


 // If the JSON object does not yet have a parse method, give it one.

     if (typeof JSON.parse !== "function") {
         JSON.parse = function (text, reviver) {

 // The parse method takes a text and an optional reviver function, and returns
 // a JavaScript value if the text is a valid JSON text.

             var j;

             function walk(holder, key) {

 // The walk method is used to recursively walk the resulting structure so
 // that modifications can be made.

                 var k;
                 var v;
                 var value = holder[key];
                 if (value && typeof value === "object") {
                     for (k in value) {
                         if (Object.prototype.hasOwnProperty.call(value, k)) {
                             v = walk(value, k);
                             if (v !== undefined) {
                                 value[k] = v;
                             } else {
                                 delete value[k];
                             }
                         }
                     }
                 }
                 return reviver.call(holder, key, value);
             }


 // Parsing happens in four stages. In the first stage, we replace certain
 // Unicode characters with escape sequences. JavaScript handles many characters
 // incorrectly, either silently deleting them, or treating them as line endings.

             text = String(text);
             rx_dangerous.lastIndex = 0;
             if (rx_dangerous.test(text)) {
                 text = text.replace(rx_dangerous, function (a) {
                     return (
                         "\\u"
                         + ("0000" + a.charCodeAt(0).toString(16)).slice(-4)
                     );
                 });
             }

 // In the second stage, we run the text against regular expressions that look
 // for non-JSON patterns. We are especially concerned with "()" and "new"
 // because they can cause invocation, and "=" because it can cause mutation.
 // But just to be safe, we want to reject all unexpected forms.

 // We split the second stage into 4 regexp operations in order to work around
 // crippling inefficiencies in IE's and Safari's regexp engines. First we
 // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
 // replace all simple value tokens with "]" characters. Third, we delete all
 // open brackets that follow a colon or comma or that begin the text. Finally,
 // we look to see that the remaining characters are only whitespace or "]" or
 // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.

             if (
                 rx_one.test(
                     text
                         .replace(rx_two, "@")
                         .replace(rx_three, "]")
                         .replace(rx_four, "")
                 )
             ) {

 // In the third stage we use the eval function to compile the text into a
 // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
 // in JavaScript: it can begin a block or an object literal. We wrap the text
 // in parens to eliminate the ambiguity.

                 j = eval("(" + text + ")");

 // In the optional fourth stage, we recursively walk the new structure, passing
 // each name/value pair to a reviver function for possible transformation.

                 return (typeof reviver === "function")
                     ? walk({"": j}, "")
                     : j;
             }

 // If the text is not JSON parseable, then a SyntaxError is thrown.

             throw new SyntaxError("JSON.parse");
         };
     }
 }());
// ==================== |--------| ====================
// ==================== | Matrix | ====================
// ==================== |--------| ====================

/*!
  2D Transformation Matrix v2.7.5 LT
  (c) Epistemex.com 2014-2018
  License: MIT
*/

/**
	* 2D transformation matrix object initialized with identity matrix.
	* @class
	* @name Matrix
	* @classdesc 2D Transformation Matrix.
	* @prop {number} a - scale x
	* @prop {number} b - shear y
	* @prop {number} c - shear x
	* @prop {number} d - scale y
	* @prop {number} e - translate x
	* @prop {number} f - translate y
	* @author Epistemex
	* @version 2.7.5
	* @license MIT license (header required)
	* @copyright Epistemex.com 2014-2018
	* @category DuESF
    * @subcategory Third Party
 */
function Matrix() {

	var me = this, _el;
	me._t = me.transform;

	me.a = me.d = 1;
	me.b = me.c = me.e = me.f = 0;
}

 /**
  * Create and transform a new matrix based on given matrix values, or
  * provide SVGMatrix or a (2D) DOMMatrix, WebKitCSSMatrix or another
  * instance of a generic matrix.
  *
  * @example
  *
  * var m = Matrix.from(1, 0.2, 0, 2, 120, 97);
  * var m = Matrix.from(domMatrix, ctx);
  * var m = Matrix.from(svgMatrix);
  * var m = Matrix.from(matrix);
  *
  * @param {*} a - number representing a (scale x) in [a-f], or a Matrix object containing properties a-f.
  * @param {*} [b] - b property (shear y) if a is not a matrix object, or optional canvas 2D context.
  * If vector is input this will be pre-translate for x.
  * @param {number} [c] - c property (shear x)
  * @param {number} [d] - d property (scale y)
  * @param {number} [e] - e property (translate x)
  * @param {number} [f] - f property (translate y)
  * @param {CanvasRenderingContext2D} [context] - optional canvas context to synchronize
  * @returns {Matrix} - new Matrix instance
  * @static
  */
Matrix.from = function(a, b, c, d, e, f, context) {

	var m = new Matrix(context), scale, dist, q;

	if (typeof a === "number")
	 m.setTransform(a, b, c, d, e, f);

	else {
	 if (typeof a.is2D === "boolean" && !a.is2D) throw "Cannot use 3D DOMMatrix.";
	 if (b) m.context = b;
	 m.multiply(a)
	}

	return m
 };

Matrix.prototype = {

	/**
	* Short-hand to reset current matrix to an identity matrix.
	* @returns {Matrix}
	*/
	reset: function() {
	 return this.setTransform(1, 0, 0, 1, 0, 0)
	},

	/**
	* Rotates current matrix by angle (accumulative).
	* @param {number} angle - angle in degrees
	* @returns {Matrix}
	*/
	rotate: function(angle) {
	 angle = DuMath.toRadians(angle);
	 var
		cos = Math.cos(angle),
		sin = Math.sin(angle);

	 return this._t(cos, sin, -sin, cos, 0, 0)
	},

	/**
	* Converts a vector given as `x` and `y` to angle, and
	* rotates (accumulative). x can instead contain an object with
	* properties x and y and if so, y parameter will be ignored.
	* @param {number|*} x
	* @param {number} [y]
	* @returns {Matrix}
	*/
	rotateFromVector: function(x, y) {
	 return this.rotate(typeof x === "number" ? Math.atan2(y, x) : Math.atan2(x.y, x.x))
	},

	/**
	* Scales current matrix accumulative.
	* @param {number[]} s - scale factor [x, y]. 1 does nothing, any third value (Z) is ignored.
	* @returns {Matrix}
	*/
	scale: function(s) {
	 return this._t(s[0], 0, 0, s[1], 0, 0);
	},

	/**
	* Apply shear to the current matrix accumulative.
	* @param {number} sx - amount of shear for x
	* @param {number} sy - amount of shear for y
	* @returns {Matrix}
	*/
	shear: function(sx, sy) {
	 return this._t(1, sy, sx, 1, 0, 0)
	},

	/**
	* Apply skew to the current matrix accumulative. Angles in radians.
	* Also see [`skewDeg()`]{@link Matrix#skewDeg}.
	* @param {number} ax - angle of skew for x
	* @param {number} ay - angle of skew for y
	* @returns {Matrix}
	*/
	skew: function(ax, ay) {
	 return this.shear(Math.tan(ax), Math.tan(ay))
	},

	/**
	* Set current matrix to new absolute matrix.
	* @param {number} a - scale x
	* @param {number} b - shear y
	* @param {number} c - shear x
	* @param {number} d - scale y
	* @param {number} e - translate x
	* @param {number} f - translate y
	* @returns {Matrix}
	*/
	setTransform: function(a, b, c, d, e, f) {
	 var me = this;
	 me.a = a;
	 me.b = b;
	 me.c = c;
	 me.d = d;
	 me.e = e;
	 me.f = f;
	 return me._x()
	},

	/**
	* Translate current matrix accumulative.
	* @param {number[]} t - translation [x, y]. Any third value (Z) is ignored.
	* @returns {Matrix}
	*/
	translate: function(t) {
	 return this._t(1, 0, 0, 1, t[0], t[1]);
	},

	/**
	* Multiplies current matrix with new matrix values. Also see [`multiply()`]{@link Matrix#multiply}.
	*
	* @param {number} a2 - scale x
	* @param {number} b2 - skew y
	* @param {number} c2 - skew x
	* @param {number} d2 - scale y
	* @param {number} e2 - translate x
	* @param {number} f2 - translate y
	* @returns {Matrix}
	*/
	transform: function(a2, b2, c2, d2, e2, f2) {

	 var
		me = this,
		a1 = me.a,
		b1 = me.b,
		c1 = me.c,
		d1 = me.d,
		e1 = me.e,
		f1 = me.f;

	 /* matrix column order is:
	  *	a c e
	  *	b d f
	  *	0 0 1
	  */
	 me.a = a1 * a2 + c1 * b2;
	 me.b = b1 * a2 + d1 * b2;
	 me.c = a1 * c2 + c1 * d2;
	 me.d = b1 * c2 + d1 * d2;
	 me.e = a1 * e2 + c1 * f2 + e1;
	 me.f = b1 * e2 + d1 * f2 + f1;

	 return me._x()
	},

	/**
	* Multiplies current matrix with source matrix.
	* @param {Matrix|Matrix|DOMMatrix|SVGMatrix} m - source matrix to multiply with.
	* @returns {Matrix}
	*/
	multiply: function(m) {
	 return this._t(m.a, m.b, m.c, m.d, m.e, m.f)
	},

	/**
	* Get an inverse matrix of current matrix. The method returns a new
	* matrix with values you need to use to get to an identity matrix.
	* Context from parent matrix is not applied to the returned matrix.
	*
	* @param {boolean} [cloneContext=false] - clone current context to resulting matrix
	* @throws Exception is input matrix is not invertible
	* @returns {Matrix} - new Matrix instance
	*/
	inverse: function(cloneContext) {

	 var
		me = this,
		m  = new Matrix(cloneContext ? me.context : null),
		dt = me.determinant();

	 if (dt === 0) throw "Matrix not invertible.";

	 m.a = me.d / dt;
	 m.b = -me.b / dt;
	 m.c = -me.c / dt;
	 m.d = me.a / dt;
	 m.e = (me.c * me.f - me.d * me.e) / dt;
	 m.f = -(me.a * me.f - me.b * me.e) / dt;

	 return m
	},

	/**
	* Decompose the current matrix into simple transforms using QR.
	*
	* @returns {*} - an object containing current decomposed values (translate, rotation, scale, skew)
	* @see {@link https://en.wikipedia.org/wiki/QR_decomposition|More on QR decomposition}
	*/
	decompose: function() {

	 var
		me		= this,
		a		 = me.a,
		b		 = me.b,
		c		 = me.c,
		d		 = me.d,
		acos	  = Math.acos,
		atan	  = Math.atan,
		sqrt	  = Math.sqrt,
		pi		= Math.PI,

		translate = {x: me.e, y: me.f},
		rotation  = 0,
		scale	 = {x: 1, y: 1},
		skew	  = {x: 0, y: 0},

		determ	= a * d - b * c,	// determinant(), skip DRY here...
		r, s;

	 // Apply the QR-like decomposition.
	 if (a || b) {
		r = sqrt(a * a + b * b);
		rotation = b > 0 ? acos(a / r) : -acos(a / r);
		scale = {x: r, y: determ / r};
		skew.x = atan((a * c + b * d) / (r * r));
	 }
	 else if (c || d) {
		s = sqrt(c * c + d * d);
		rotation = pi * 0.5 - (d > 0 ? acos(-c / s) : -acos(c / s));
		scale = {x: determ / s, y: s};
		skew.y = atan((a * c + b * d) / (s * s));
	 }
	 else { // a = b = c = d = 0
		scale = {x: 0, y: 0};
	 }

	 return {
		translate: translate,
		rotation : rotation,
		scale	: scale,
		skew	 : skew
	 }
	},

	/**
	* Returns the determinant of the current matrix.
	* @returns {number}
	*/
	determinant: function() {
	 return this.a * this.d - this.b * this.c
	},

	/**
	* Apply current matrix to `x` and `y` of a point.
	* Returns a point object.
	*
	* @param {number[]} pt - the point to transform ([x, y]).<br />
	* If an optionnal Z value is provided, it will be kept without transformation.
	* @returns {number[]} A new transformed point [x, y]. If pt had a third value, it is returned too, as it was without transformation.
	*/
	applyToPoint: function(pt) {
	 var me = this;
	 var x = pt[0] * me.a + pt[1] * me.c + me.e;
	 var y = pt[0] * me.b + pt[1] * me.d + me.f;
	 var result = [x,y];
	 if (pt.length == 3) result.push(pt[2]);
	 return result;
	},

	/**
	* Returns true if matrix is an identity matrix (no transforms applied).
	* @returns {boolean}
	*/
	isIdentity: function() {
	 var me = this;
	 return me.a === 1 && !me.b && !me.c && me.d === 1 && !me.e && !me.f
	},

	/**
	* Returns true if matrix is invertible
	* @returns {boolean}
	*/
	isInvertible: function() {
	 return !this._q(this.determinant(), 0)
	},

	/**
	* The method is intended for situations where scale is accumulated
	* via multiplications, to detect situations where scale becomes
	* "trapped" with a value of zero. And in which case scale must be
	* set explicitly to a non-zero value.
	*
	* @returns {boolean}
	*/
	isValid: function() {
	 return !(this.a * this.d)
	},

	/**
	* Compares current matrix with another matrix. Returns true if equal
	* (within epsilon tolerance).
	* @param {Matrix|Matrix|DOMMatrix|SVGMatrix} m - matrix to compare this matrix with
	* @returns {boolean}
	*/
	isEqual: function(m) {

	 var
		me = this,
		q = me._q;

	 return  q(me.a, m.a) &&
			 q(me.b, m.b) &&
			 q(me.c, m.c) &&
			 q(me.d, m.d) &&
			 q(me.e, m.e) &&
			 q(me.f, m.f)
	},

	/**
	* Clones current instance and returning a new matrix.
	* @param {boolean} [noContext=false] don't clone context reference if true
	* @returns {Matrix} - a new Matrix instance with identical transformations as this instance
	*/
	clone: function(noContext) {
	 return new Matrix(noContext ? null : this.context).multiply(this)
	},

	/**
	* Generates a string that can be used with CSS `transform`.
	* @example
	*	 element.style.transform = m.toCSS();
	* @returns {string}
	*/
	toCSS: function() {
	 return "matrix(" + this.toArray() + ")"
	},

	/**
	* Generates a `matrix3d()` string that can be used with CSS `transform`.
	* Although the matrix is for 2D use you may see performance benefits
	* on some devices using a 3D CSS transform instead of a 2D.
	* @example
	*	 element.style.transform = m.toCSS3D();
	* @returns {string}
	*/
	toCSS3D: function() {
	 var me = this;
	 return "matrix3d(" + me.a + "," + me.b + ",0,0," + me.c + "," + me.d + ",0,0,0,0,1,0," + me.e + "," + me.f + ",0,1)"
	},

	/**
	* Returns a JSON compatible string of current matrix.
	* @returns {string}
	*/
	toJSON: function() {
	 var me = this;
	 return '{"a":' + me.a + ',"b":' + me.b + ',"c":' + me.c + ',"d":' + me.d + ',"e":' + me.e + ',"f":' + me.f + '}'
	},

	/**
	* Compares floating point values with some tolerance (epsilon)
	* @param {number} f1 - float 1
	* @param {number} f2 - float 2
	* @returns {boolean}
	* @private
	*/
	_q: function(f1, f2) {
	 return Math.abs(f1 - f2) < 1e-14
	},

	/**
	* Apply current absolute matrix to context if defined, to sync it.
	* Apply current absolute matrix to element if defined, to sync it.
	* @returns {Matrix}
	* @private
	*/
	_x: function() {

	 var me = this;

	 if (me.context)
		me.context.setTransform(me.a, me.b, me.c, me.d, me.e, me.f);

	 return me
	}
};
// ==================== |------------| ====================
// ==================== | SeedRandom | ====================
// ==================== |------------| ====================

/**
 * Adds a new Math.seedRandom() method, used as a workaround for a bug in Math.random() with After Effects on Mac OS
 * @name seedRandom
 * @author David Bau
 * @copyright David Bau
 * @license MIT
 * @category DuESF
 * @subcategory Third Party
 */

!function(a,b){function c(c,j,k){var n=[];j=1==j?{entropy:!0}:j||{};var s=g(f(j.entropy?[c,i(a)]:null==c?h():c,3),n),t=new d(n),u=function(){for(var a=t.g(m),b=p,c=0;q>a;)a=(a+c)*l,b*=l,c=t.g(1);for(;a>=r;)a/=2,b/=2,c>>>=1;return(a+c)/b};return u.int32=function(){return 0|t.g(4)},u.quick=function(){return t.g(4)/4294967296},u["double"]=u,g(i(t.S),a),(j.pass||k||function(a,c,d,f){return f&&(f.S&&e(f,t),a.state=function(){return e(t,{})}),d?(b[o]=a,c):a})(u,s,"global"in j?j.global:this==b,j.state)}function d(a){var b,c=a.length,d=this,e=0,f=d.i=d.j=0,g=d.S=[];for(c||(a=[c++]);l>e;)g[e]=e++;for(e=0;l>e;e++)g[e]=g[f=s&f+a[e%c]+(b=g[e])],g[f]=b;(d.g=function(a){for(var b,c=0,e=d.i,f=d.j,g=d.S;a--;)b=g[e=s&e+1],c=c*l+g[s&(g[e]=g[f=s&f+b])+(g[f]=b)];return d.i=e,d.j=f,c})(l)}function e(a,b){return b.i=a.i,b.j=a.j,b.S=a.S.slice(),b}function f(a,b){var c,d=[],e=typeof a;if(b&&"object"==e)for(c in a)try{d.push(f(a[c],b-1))}catch(g){}return d.length?d:"string"==e?a:a+"\0"}function g(a,b){for(var c,d=a+"",e=0;e<d.length;)b[s&e]=s&(c^=19*b[s&e])+d.charCodeAt(e++);return i(b)}function h(){try{if(j)return i(j.randomBytes(l));var b=new Uint8Array(l);return(k.crypto||k.msCrypto).getRandomValues(b),i(b)}catch(c){var d=k.navigator,e=d&&d.plugins;return[+new Date,k,e,k.screen,i(a)]}}function i(a){return String.fromCharCode.apply(0,a)}var j,k=this,l=256,m=6,n=52,o="random",p=b.pow(l,m),q=b.pow(2,n),r=2*q,s=l-1;if(b["seed"+o]=c,g(b.random(),a),"object"==typeof module&&module.exports){module.exports=c;try{j=require("crypto")}catch(t){}}else"function"==typeof define&&define.amd&&define(function(){return c})}([],Math);
// ==================== |-------------| ====================
// ==================== | colorPicker | ====================
// ==================== |-------------| ====================

;(function(){
/**
 * @classdesc ColorPicker v2.0 for Adobe scripting.<br />
 * 2016-5-11 -> 2016-7-24<br />
 * This is a rebuilt color picker for Adobe scripting.<br />
 * Support all Adobe softwares such as PS,AI,PR and so on.<br />
 * See usage on {@link http://github.com/Smallpath/AdobeColorPicker}
 * @class
 * @name colorPicker
 * @author smallpath
 * @copyright smallpath
 * @license MIT
 * @link https://github.com/Smallpath/AdobeColorPicker
 * @category DuESF
 * @subcategory Third Party
 */
function colorPicker(inputColour,options){
    if(!(this instanceof colorPicker))
        return new colorPicker(inputColour,options)

    this.options = {
        name : "Adobe Color Picker ",
        version : "v2.0",
        shouldUpdateCursor: false,
        backupLocation : [],

        windowType:"dialog",  // "dialog","palette" and the reference of Panel
    }

    if(options && colorPicker.isType(options,"Object")){
        for(var i in options)
            this.options[i] = options[i];
    }
    
    this.inputColour = colorPicker.parseColor(inputColour);
    this.outputColour = this.inputColour.slice(0);
    this.initSetting();
    return this.showColorPicker();
}

colorPicker.parseColor = function(inputValue){
        if(!inputValue)
            return [1,1,1];
            
        if(colorPicker.isRgb(inputValue))                   //[0,0,0] - [1,1,1] 
            return colorPicker.parseRgb(inputValue);
        else if(colorPicker.isLargeRgb(inputValue))    //[0,0,0] - [255,255,255]
            return colorPicker.parseLargeRgb(inputValue);
        else if(colorPicker.isHex(inputValue) )            //FFFFFF
            return colorPicker.parseHex(inputValue);
        else if(colorPicker.isShortHex(inputValue))     //FFF
            return colorPicker.parseShortHex(inputValue);
        else if(colorPicker.isHsb(inputValue))             //[0,0,0,'hsb'] - [360,100,100,'hsb']
            return colorPicker.parseHsb(inputValue);
        else
            return [1,1,1];
}

colorPicker.prototype.showColorPicker =  function(){
        var win = this.initWindow();
        if(win.type == "dialog" ||win.type == "palette" ){
            if(this.haveSetting ("location")){
                win.location = this.getSetting ("location").split(",");
                if(win.location.length != 2)
                    win.center();
                else if(win.location[0]<0 || win.location[1]<0)
                    win.center();
            } 
            win.show();
            this.saveSetting ("location", win.location);
        }else if(win.type == "panel"){
            win.layout.layout(true);
        }
        if(!this.isSmallMode){
            this.outputColour.hex = colorPicker.RgbToHex(this.outputColour);
            this.outputColour.hsb = colorPicker.RgbToHsb(this.outputColour);
            this.outputColour.rgb = this.outputColour.slice(0);
            
            return this.outputColour;
        }else{
            return {
                window:win, 
                colorPicker:this
            };
        }
}

colorPicker.prototype.initWindow = function(){
            var _this = this;
            var type = this.options["windowType"];
            if(type=="palette"){
                    var win = new Window("palette", this.options["name"] + this.options["version"], undefined, {
                            maximizeButton: false,
                            minimizeButton: false,
                        });
                    this.size = this.options["size"] = 90;
            }else if(type instanceof Panel){
                    var win = type;
                    this.size = this.options["size"] = 90;
            }else{
                    var win = new Window("dialog", this.options["name"] + this.options["version"], undefined, {
                        maximizeButton: false,
                        minimizeButton: false,
                        closeButton:false
                    });
                    this.size = this.options["size"] = 130;
            }
        
            var isSmallMode = this.isSmallMode = this.size != 130;
            var targetImage = isSmallMode ? this.options["smallColorWheel"]:  this.img ;
            var targetSize = this.size*2;

            var colourGroup = win.add('group');
            	colourGroup.orientation = "stack";
                    
	    		win.image = colourGroup.add("image", undefined,targetImage);

	    		var colourCursorGroup = this.colourCursorGroup = colourGroup.add('customBoundedValue',[0,0,targetSize+2,targetSize+2]);
		    		colourCursorGroup.fillColour = [0,0,0,0];

                 var colourSelectCursor = this.colourSelectCursor = colourCursorGroup.colourSelectCursor = {};
                        colourSelectCursor.size = [12,12];
                        colourSelectCursor.strokeWidth = 1;
                        colourSelectCursor.strokeColour = [0,0,0];
                        
                        this.setCursorLocation (this.inputColour);
                        
                colourCursorGroup.onDraw = function () {
		                this.graphics.drawOSControl();
		                this.graphics.newPath();
		                this.graphics.ellipsePath(0,0,this.size[0],this.size[1]);
		                this.graphics.fillPath(colourCursorGroup.graphics.newBrush(colourCursorGroup.graphics.BrushType.SOLID_COLOR, colourCursorGroup.fillColour));
                        
		                this.graphics.newPath();
                        
		                this.graphics.ellipsePath(this.colourSelectCursor.location[0]+this.colourSelectCursor.strokeWidth/2+1,
                                                                        this.colourSelectCursor.location[1]+this.colourSelectCursor.strokeWidth/2+1,
                                                                        this.colourSelectCursor.size[0]-this.colourSelectCursor.strokeWidth,
                                                                        this.colourSelectCursor.size[1]-this.colourSelectCursor.strokeWidth);
		                this.graphics.strokePath(this.graphics.newPen(this.graphics.PenType.SOLID_COLOR, 
                                                                   this.colourSelectCursor.strokeColour, this.colourSelectCursor.strokeWidth));
		    		};  


            win.brightGroup = win.add("group");
            win.staticBright = win.brightGroup.add("statictext",undefined,"Bright:");
            win.editBright = win.brightGroup.add("edittext{text:'0',characters:3,justify:'center',active:1}");
            win.slider = win.brightGroup.add("slider",undefined,100,0,100); 
            if(!isSmallMode){
                win.slider.size = "width:160,height:20";
                var spacing = 10;
                var character = 4;
            }else{
                win.slider.size = "width:100,height:20";
                win.brightGroup.spacing = 2;
                win.spacing = 5;
                win.margins = 2;
                var spacing = 0;
                var character = 3;
            }


            var editor = win.add('group');
                editor.orientation = 'column';

                editor.gulu = editor.add('group');

                    editor.gulu.uni = editor.gulu.add('group');
                    editor.gulu.uni.spacing = spacing;

                        editor.gulu.uni.Ed = editor.gulu.uni.add('statictext', undefined, '#');
                        editor.gulu.uni.unicode = editor.gulu.uni.add('edittext', undefined, 'FF0000')
                        editor.gulu.uni.unicode.characters = 6;
                        editor.gulu.uni.unicode.justify = 'center';

                    editor.gulu.color = editor.gulu.add('customBoundedValue', undefined, 'Redraw original image');
                    editor.gulu.color.size = [80, 25];

                editor.colorHolder = editor.add('group');
                editor.colorHolder.orientation = 'row';

                    editor.colorHolder.colorCol1 = editor.colorHolder.add('group');
                    editor.colorHolder.colorCol1.orientation = 'column';

                        editor.colorHolder.colorCol1.hGroup = editor.colorHolder.colorCol1.add('group');
                        editor.colorHolder.colorCol1.hGroup.spacing = spacing;

                            editor.colorHolder.colorCol1.hGroup.hRad = editor.colorHolder.colorCol1.hGroup.add('statictext', undefined, 'H:');
                            editor.colorHolder.colorCol1.hGroup.hValue = editor.colorHolder.colorCol1.hGroup.add('edittext', undefined, '0');
                            editor.colorHolder.colorCol1.hGroup.hValue.characters = character;
                            editor.colorHolder.colorCol1.hGroup.hValue.justify = 'center';
                            editor.colorHolder.colorCol1.hGroup.hValue._index = 0;

                        editor.colorHolder.colorCol1.rGroup = editor.colorHolder.colorCol1.add('group');
                        editor.colorHolder.colorCol1.rGroup.spacing = spacing;

                            editor.colorHolder.colorCol1.rGroup.rRad = editor.colorHolder.colorCol1.rGroup.add('statictext', undefined, 'R:');
                            editor.colorHolder.colorCol1.rGroup.rValue = editor.colorHolder.colorCol1.rGroup.add('edittext', undefined, '0');
                            editor.colorHolder.colorCol1.rGroup.rValue.characters = character;
                            editor.colorHolder.colorCol1.rGroup.rValue.justify = 'center';
                            editor.colorHolder.colorCol1.rGroup.rValue._index = 0;

                    editor.colorHolder.colorCol2 = editor.colorHolder.add('group');
                    editor.colorHolder.colorCol2.orientation = 'column';

                        editor.colorHolder.colorCol2.sGroup = editor.colorHolder.colorCol2.add('group');
                        editor.colorHolder.colorCol2.sGroup.spacing = spacing;

                            editor.colorHolder.colorCol2.sGroup.sRad = editor.colorHolder.colorCol2.sGroup.add('statictext', undefined, 'S:');
                            editor.colorHolder.colorCol2.sGroup.sValue = editor.colorHolder.colorCol2.sGroup.add('edittext', undefined, '0');
                            editor.colorHolder.colorCol2.sGroup.sValue.characters = character;
                            editor.colorHolder.colorCol2.sGroup.sValue.justify = 'center';
                            editor.colorHolder.colorCol2.sGroup.sValue._index = 1;

                        editor.colorHolder.colorCol2.gGroup = editor.colorHolder.colorCol2.add('group');
                        editor.colorHolder.colorCol2.gGroup.spacing = spacing;

                            editor.colorHolder.colorCol2.gGroup.gRad = editor.colorHolder.colorCol2.gGroup.add('statictext', undefined, 'G:');
                            editor.colorHolder.colorCol2.gGroup.gValue = editor.colorHolder.colorCol2.gGroup.add('edittext', undefined, '0');
                            editor.colorHolder.colorCol2.gGroup.gValue.characters = character;
                            editor.colorHolder.colorCol2.gGroup.gValue.justify = 'center';
                            editor.colorHolder.colorCol2.gGroup.gValue._index = 1;

                    editor.colorHolder.colorCol3 = editor.colorHolder.add('group');
                    editor.colorHolder.colorCol3.orientation = 'column';

                        editor.colorHolder.colorCol3.lGroup = editor.colorHolder.colorCol3.add('group');
                        editor.colorHolder.colorCol3.lGroup.spacing = spacing;

                            editor.colorHolder.colorCol3.lGroup.lRad = editor.colorHolder.colorCol3.lGroup.add('statictext', undefined, 'B:');
                            editor.colorHolder.colorCol3.lGroup.lValue = editor.colorHolder.colorCol3.lGroup.add('edittext', undefined, '0');
                            editor.colorHolder.colorCol3.lGroup.lValue.characters = character;
                            editor.colorHolder.colorCol3.lGroup.lValue.justify = 'center';
                            editor.colorHolder.colorCol3.lGroup.lValue._index = 2;

                        editor.colorHolder.colorCol3.bGroup = editor.colorHolder.colorCol3.add('group');
                        editor.colorHolder.colorCol3.bGroup.spacing = spacing;

                            editor.colorHolder.colorCol3.bGroup.bRad = editor.colorHolder.colorCol3.bGroup.add('statictext', undefined, 'B:');
                            editor.colorHolder.colorCol3.bGroup.bValue = editor.colorHolder.colorCol3.bGroup.add('edittext', undefined, '0');
                            editor.colorHolder.colorCol3.bGroup.bValue.characters = character;
                            editor.colorHolder.colorCol3.bGroup.bValue.justify = 'center';
                            editor.colorHolder.colorCol3.bGroup.bValue._index = 2;


                win.editor = editor;
                if(win.type == "dialog"){

                    
                    editor.oc = win.editor.oc = win.add("Group{ok:Button{text:'Ok'},can:Button{text:'Cancel'}}");
                    
                        editor.oc.ok.onClick = function(){
                            win.close();
                        }

                        editor.oc.can.onClick = function(){
                            colorPicker.copyArr(_this.outputColour,_this.inputColour);
                            win.close();
                        }
                    
                    }

            editor.gulu.color.onDraw = function(draw){
            	var targetColour = _this.outputColour;

                var gfxs=this.graphics;
                gfxs.newPath();
                gfxs.rectPath(0, 0, this.size[0], this.size[1]);
                gfxs.fillPath(gfxs.newBrush (gfxs.BrushType.SOLID_COLOR, targetColour));
            };




        
            this.updateCursor(win);

            this.setDefaultValue(win)

            this.bindingKeydown(win);

            this.bindingHandler(win);

            return win;

}

colorPicker.prototype.setDefaultValue = function(win){
        var pi = win.editor;
        var startColour = this.outputColour;


        pi.gulu.uni.unicode.text= colorPicker.RgbToHex (startColour);
        pi.gulu.uni.unicode.active = true;



        pi.colorHolder.colorCol1.rGroup.rValue.text=Math.round(startColour[0]*255);
        pi.colorHolder.colorCol2.gGroup.gValue.text=Math.round(startColour[1]*255);
        pi.colorHolder.colorCol3.bGroup.bValue.text=Math.round(startColour[2]*255);

        var hsbHere= colorPicker.RgbToHsb([
                                        startColour[0]*255,
                                        startColour[1]*255,
                                        startColour[2]*255
                                    ]);
                                    

        pi.colorHolder.colorCol1.hGroup.hValue.text=hsbHere[0];
        pi.colorHolder.colorCol2.sGroup.sValue.text=hsbHere[1];
        pi.colorHolder.colorCol3.lGroup.lValue.text=hsbHere[2];

        win.slider.value=hsbHere[2];
        win.editBright.text=hsbHere[2];
        
        this.colourCursorGroup.fillColour[3] = 1 - (hsbHere[2])/100;
        this.colourCursorGroup.notify("onDraw");
}

colorPicker.prototype.bindingHandler =  function(win){
           var _this = this;

          win.editor.colorHolder.colorCol1.hGroup.hValue.onChange =
          win.editor.colorHolder.colorCol2.sGroup.sValue.onChange =
          win.editor.colorHolder.colorCol3.lGroup.lValue.onChange = function(){
                    _this.options.backupLocation.length = 0;
                    this.text=Math.round(this.text);

                    if(this._index ==0 ){
                        if( this.text<0 || this.text>360 || isNaN(this.text)==true ){
                                this.text= colorPicker.RgbToHsb([
                                                                                    _this.outputColour[0]*255,
                                                                                    _this.outputColour[1]*255,
                                                                                    _this.outputColour[2]*255,
                                                                                ])[this._index];
                       }
                    }else{
                        if( this.text<0 || this.text>100 || isNaN(this.text)==true ){
                                this.text= colorPicker.RgbToHsb([
                                                                                    _this.outputColour[0]*255,
                                                                                    _this.outputColour[1]*255,
                                                                                    _this.outputColour[2]*255,
                                                                                ])[this._index];
                       }
                    }
                    var hsbArr = [
                                          Math.round(win.editor.colorHolder.colorCol1.hGroup.hValue.text),
                                          Math.round(win.editor.colorHolder.colorCol2.sGroup.sValue.text),
                                          Math.round(win.editor.colorHolder.colorCol3.lGroup.lValue.text)
                                        ];

                    var rgbArr = colorPicker.HsbToRgb (hsbArr);
                    var hexStr = colorPicker.RgbToHex ([rgbArr[0]/255,rgbArr[1]/255,rgbArr[2]/255]);
                    win.editor.gulu.uni.unicode.text = hexStr;
                    win.editor.gulu.uni.unicode.notify("onChange");
                     
          }


           win.editor.colorHolder.colorCol1.rGroup.rValue.onChange  =
           win.editor.colorHolder.colorCol2.gGroup.gValue.onChange =
           win.editor.colorHolder.colorCol3.bGroup.bValue.onChange = function (){
                    _this.options.backupLocation.length = 0;
                    this.text=Math.round(this.text);

                    if( this.text<0 || this.text>255 || isNaN(this.text)==true ){
                            this.text=Math.round(_this.outputColour[this._index]*255);
                   }

                    if(this._index ==0)
                        win.editor.gulu.uni.unicode.text= colorPicker.RgbToHex ([this.text/255,_this.outputColour[1],_this.outputColour[2]]);
                    else if(this._index ==1)
                        win.editor.gulu.uni.unicode.text= colorPicker.RgbToHex ([_this.outputColour[0],this.text/255,_this.outputColour[2]]);
                    else if(this._index ==2)
                        win.editor.gulu.uni.unicode.text= colorPicker.RgbToHex ([_this.outputColour[0],_this.outputColour[1],this.text/255]);
                     win.editor.gulu.uni.unicode.notify("onChange");
                     
               }

           win.editBright.onChange = win.editBright.onChanging = function () {
                    _this.options.backupLocation.length = 0;
           		if (this.text < 0)
           			this.text = 0;
           		if (this.text > 100)
           			this.text = 100;
           		if (isNaN(this.text)==true)
           			this.text = 100;

           		win.slider.value = parseInt(this.text);
                    if(isNaN(win.slider.value))
                        return;
           		win.slider.notify('onChange');
           };

           win.slider.onChange = win.slider.onChanging = function(){

                    var thisColor= colorPicker.HsbToRgb ([
                                                            Math.round(win.editor.colorHolder.colorCol1.hGroup.hValue.text),
                                                            Math.round(win.editor.colorHolder.colorCol2.sGroup.sValue.text),
                                                            Math.round(this.value)
                                                        ]);
                    if(this.value != 0){
                        if(_this.options.backupLocation.length != 0 && _this.options.shouldUpdateCursor == true){
                            _this.getColor({
                                    type:"mouseup",
                                    clientX: _this.options.backupLocation[0]+6,
                                    clientY: _this.options.backupLocation[1]+6,
                            });
                            _this.options.backupLocation.length = 0;
                            _this.colourCursorGroup.fillColour[3] = 1 - (this.value)/100;
                            _this.colourCursorGroup.notify("onDraw");
                            return;
                        }
                    }                
                                             
                    colorPicker.copyArr(_this.outputColour,[thisColor[0]/255,thisColor[1]/255,thisColor[2]/255]);

                    _this.setDefaultValue(win);
                    _this.notifyColor(win);
                	_this.updateCursor(win);
                    
                    _this.setCursorLocation (_this.outputColour);

                    if(this.value == 0){
                        _this.options.shouldUpdateCursor = true;
                    }else{
                        _this.options.shouldUpdateCursor = false;
                    }
                            
                            
                    if(_this.options.backupLocation.length == 0){
                        colorPicker.copyArr(_this.options.backupLocation,_this.colourSelectCursor.location);
                    }

                    _this.colourCursorGroup.fillColour[3] = 1 - (this.value)/100;
                    _this.colourCursorGroup.notify("onDraw");
                     
               }

            win.editor.gulu.uni.unicode.onChange= function(){
                    var hexHere="0x"+this.text;
                    var eV=0;
                    if(colorPicker.isHex (this.text) == false && colorPicker.isShortHex(this.text) == false){
                        this.text = colorPicker.RgbToHex (_this.outputColour);
                        eV=1;
                    }
                    if(eV==0){
                            var rgbHere= (this.text.length == 6) ? colorPicker.parseHex (this.text): colorPicker.parseShortHex (this.text);
                            colorPicker.copyArr(_this.outputColour,rgbHere);
                            _this.setDefaultValue(win);
                            _this.setCursorLocation(rgbHere);
                            _this.notifyColor(win);
                        }
                }

}

colorPicker.prototype.updateCursor = function(win) {
    if (colorPicker.arraysEqual(this.colourSelectCursor.strokeColour,[1,1,1])) {
		if (win.slider.value > 63)
	    	this.colourSelectCursor.strokeColour = [0,0,0];
	} else if (colorPicker.arraysEqual(this.colourSelectCursor.strokeColour,[0,0,0])) {
		if (win.slider.value <= 63)
			this.colourSelectCursor.strokeColour = [1,1,1];
	}
};

colorPicker.prototype.notifyColor = function(win){
    win.editor.gulu.color.notify("onDraw");
}

colorPicker.prototype.setCursorLocation= function(inputColor){
     this.colourSelectCursor.location = (function(_this){
            var hsb = colorPicker.RgbToHsb(inputColor);  
            hsb = colorPicker.convertHsbToKulerHsb(hsb);
            var angle = Math.round(hsb[0]); 
            var length = Math.round(hsb[1]/100*_this.size);
                            
            var point = [length*Math.cos(angle*2*Math.PI/360),length*Math.sin(angle*2*Math.PI/360)];

            return [point[0]+_this.size,_this.size-point[1]]
      })(this)
    
      this.colourSelectCursor.location = [this.colourSelectCursor.location[0]-this.colourSelectCursor.size[0]/2,
                                                         this.colourSelectCursor.location[1]-this.colourSelectCursor.size[1]/2];    
};

colorPicker.prototype.bindingKeydown = function(win){
    var _this = this;


    var keyDownHandle1 = function(k){
        if (k.keyName == "Up") {
            if (k.shiftKey == false) {
                this.text = parseFloat(this.text) + 1;
            } else {
                this.text = parseFloat(this.text) + 10;
            }
        } else if (k.keyName == "Down") {
            if (k.shiftKey == false) {
                this.text = parseFloat(this.text) - 1;
            } else {
                this.text = parseFloat(this.text) - 10;
            }
        }
    }

    win.editor.colorHolder.colorCol1.rGroup.rValue.addEventListener('keydown', keyDownHandle1);
    win.editor.colorHolder.colorCol2.gGroup.gValue.addEventListener('keydown', keyDownHandle1);
    win.editor.colorHolder.colorCol3.bGroup.bValue.addEventListener('keydown', keyDownHandle1);
    win.editBright.addEventListener('keydown', keyDownHandle1);

    win.addEventListener('keydown', function (k) {
        if (k.keyName == "Escape") {
            win.close();
        }
    });

    var leftPressed = false;

    var getColor = this.getColor = function (k) {
        _this.options.backupLocation.length = 0;
        
        if(k.type == "mouseup"){
                leftPressed = false;
        }else if(k.type == "mousemove"){
                if(leftPressed == false)
                    return;
        }else if(k.type == "mousedown"){
                leftPressed = true;
        }
        var point = [k.clientX, k.clientY];
        if(!_this.isInCircle(point)) return;

        var thisColor = _this.getColorFromPoint(point);
        thisColor = colorPicker.RgbToHsb(thisColor);
        thisColor[2] = win.slider.value;
        thisColor = colorPicker.HsbToRgb(thisColor);

        colorPicker.copyArr(_this.outputColour ,[
                                            thisColor[0]/255,
                                            thisColor[1]/255,
                                            thisColor[2]/255
        ]);
        _this.setCursorLocation (_this.outputColour);
        _this.setDefaultValue(win);
        win.editor.gulu.color.notify("onDraw");
    }

    this.colourCursorGroup.addEventListener ('mouseup', getColor)
    this.colourCursorGroup.addEventListener ('mousemove',getColor)
    this.colourCursorGroup.addEventListener ('mousedown',getColor)
}

colorPicker.prototype.isInCircle = function(point){
    return Math.pow (point[0]-this.size, 2) + Math.pow(point[1]-this.size,2) <= Math.pow(this.size,2);
}

colorPicker.prototype.getColorFromPoint =  function(point){
    var transformedPoint = this.transformPoint(point);
    var hAndS =  this.getAngleAndLength(transformedPoint);
    return this.CoreGetColorFromPoint(hAndS[0],hAndS[1]);
}

colorPicker.prototype.getAngleAndLength = function(point){
        var angle,length;
        var x = point[0],y=point[1];
        length =Math.sqrt( x*x + y*y);
        angle = Math.atan2(y,x)/Math.PI*180
        if(angle<=0)
            angle += 360;
        return [angle,length/this.size];
    }

colorPicker.prototype.transformPoint = function(point){
        var x = point[0];
        var y = point[1];
        return [x-this.size,this.size-y];
    },


colorPicker.prototype.CoreGetColorFromPoint = function(h,s){
    var i;
    
    var f, p, q, t;
    var r=1,g=1,b=1;
    var v = 1;
    if( s == 0 ) {
        v = Math.floor(v*255);
        return [v,v,v];
        }
    var originHeight = h;
    var condition;
    var tempI
    
    if(originHeight<45 && originHeight>=0){
        i = 0;      
        f = originHeight/90; 
    }else if(originHeight<120 && originHeight>=45){ 
        i = 1 
        f = (originHeight-45)/(120-45); 
    }else if(originHeight<180 && originHeight>=120){ 
        i = 2 
        f = (originHeight-120)/(180-120); 
    }else if(originHeight<220 && originHeight>=180){ 
        i = 3 
        f = (originHeight-180)/(220-180); 
    }else if(originHeight<275 && originHeight>=220){ 
        i = 4  
        f = (originHeight-220)/(275-220); 
    }else if(originHeight<320 && originHeight>=275){ 
        i = 5  
        f = (originHeight-275)/(320-275);
    }else if(originHeight<360 && originHeight>=320){ 
        i = 6
        f = (originHeight-320)/(360-320)
    }

    p =  1 - s ;
    q =  1 - s * f ;
    t =  1 - s * ( 1 - f );
    
    switch( i ) {
        case 0:r = v;g = t;b = p;break;        
        case 1:r = v;g = 0.5+t/2;b = p;break;   
        case 2:r = q;g = v;b = p;break;     
        case 3:r = p;g = v;b = t;break;      
        case 4:r = p;g = q;b = v;break;        
        case 5:r = t;g = p;b = v;break;         
        case 6:r = v;g = p;b = q;break;         
        
    }
    return [r,g,b];
}

colorPicker.copyArr =  function(defaultArr,otherArr){
    while(defaultArr.length!=0){
        defaultArr.pop();
    }
    defaultArr.push(otherArr[0]);
    defaultArr.push(otherArr[1]);
    defaultArr.push(otherArr[2]);
    return defaultArr;
}

colorPicker.HexToRgb =  function(hex){
            var ccolorhex = hex.toString(16);
            ccolorb = parseInt(ccolorhex.substr(-2), 16);
            ccolorg = parseInt(ccolorhex.substr(-4).substr(0, 2), 16);
            ccolorr = parseInt(ccolorhex.substr(-6).substr(0, 2), 16);
            return [ccolorr / 255, ccolorg / 255, ccolorb / 255];
}

colorPicker.RgbToHex =  function(rgb){
                var a=(rgb[0]*255).toString(16);
                var b=(rgb[1]*255).toString(16);
                var c=(rgb[2]*255).toString(16);
                if(a.length!=2){
                    a="0"+a;
                    }
                if(b.length!=2){
                    b="0"+b;
                    }
                if(c.length!=2){
                    c="0"+c;
                    }
                return (a+b+c).toUpperCase();
}

colorPicker.HsbToRgb =  function(hsb){
        var rgb = [];
        hsb = [hsb[0], hsb[1] / 100, hsb[2] / 100];
        for (var offset = 240, i = 0; i < 3; i++, offset -= 120) {
            x = Math.abs((hsb[0] + offset) %360 - 240);
            if (x <= 60) {
                rgb[i] = 255;
            } else if (60 < x && x < 120) {
                rgb[i] = ((1 - (x - 60) / 60) * 255);
            } else {
                rgb[i] = 0;
            }
        }
        for (var i = 0; i < 3; i++) {
            rgb[i] += (255 - rgb[i]) * (1 - hsb[1]);
        }
        for (var i = 0; i < 3; i++) {
            rgb[i] *= hsb[2];
        }
        return [rgb[0], rgb[1], rgb[2]]
}

colorPicker.RgbToHsb = function(rgb){
        rgb = colorPicker.parseColor (rgb);
        rgb = [rgb[0]*255,rgb[1]*255,rgb[2]*255]
        var hsb = [];
        var rearranged=rgb.slice(0);
        var maxIndex = 0;
        var minIndex = 0;
        var tmp;
        rearranged.sort(function(a, b) {
            return a - b;
        })
        for (var i = 0; i < 3; i++) {
            if (rearranged[0] == rgb[i]) minIndex = i;
            if (rearranged[2] == rgb[i]) maxIndex = i;
        }
        if(rearranged[2] !=0 ){
            hsb[2] = rearranged[2] / 255;
            hsb[1] = 1 - rearranged[0] / rearranged[2];
            if(hsb[1]!=0){
                hsb[0] = maxIndex * 120 + 60 * (rearranged[1] / hsb[1] / rearranged[2] + (1 - 1 / hsb[1])) * ((maxIndex - minIndex + 3) % 3 == 1 ? 1 : -1);
                hsb[0] = (hsb[0] + 360) % 360;
            }else{
                hsb[0] =0;
            }
        }else{
            hsb[2]=0;
            hsb[1]=0;
            hsb[0]=0;
        }
        return [Math.round(hsb[0]), Math.round(hsb[1] * 100), Math.round(hsb[2] * 100)];
}

colorPicker.convertHsbToKulerHsb =  function(hsb){
    var originHeight = hsb[0];
    var s = hsb[1], b = hsb[2];
    var i,f,h;
    if(originHeight<30 && originHeight>=0){
        
        h = (originHeight-0)/(30-0)*(45-0)+0;
        
    }else if(originHeight<60 && originHeight>=30){ 
        h = (originHeight-30)/(60-30)*(120-45)+45;
        
    }else if(originHeight<120 && originHeight>=60){ 
        h = (originHeight-60)/(120-60)*(180-120)+120;
        
    }else if(originHeight<180 && originHeight>=120){ 
        h = (originHeight-120)/(180-120)*(220-180)+180;
        
    }else if(originHeight<240 && originHeight>=180){ 
        h = (originHeight-180)/(240-180)*(275-220)+220;
        
    }else if(originHeight<300 && originHeight>=240){ 
        h = (originHeight-240)/(300-240)*(320-275)+275;
        
    }else if(originHeight<360 && originHeight>=300){ 
        h = (originHeight-300)/(360-300)*(360-320)+320;
        
    }else{
      return hsb;
    }

    return [h,s,b];

}



colorPicker.isType =  function(content,type){
    return Object.prototype.toString.call(content) == "[object "+type+"]"
}

colorPicker.isRgb =  function(rgbArr){
    if(!rgbArr) 
        return false;
    
    if(!colorPicker.isType(rgbArr,"Array")) 
        return false;
    
    if(rgbArr.length != 3)
        return false;
    
    for(var i =0,len = rgbArr.length;i<len;i++){
            if(rgbArr[i]>1 || rgbArr[i]<0)
                return false;
    }
        
    return true;
}

colorPicker.isLargeRgb =  function(rgbArr){
    if(!rgbArr) 
        return false;
    
    if(!colorPicker.isType(rgbArr,"Array")) 
        return false;
        
    if(rgbArr.length != 3)
        return false;
    
    for(var i =0,len = rgbArr.length;i<len;i++){
            if(rgbArr[i]>255 || rgbArr[i]<0)
                return false;
    }

    return true;
}


colorPicker.isHex = function(hexStr){
    if(!hexStr)
        return false;
        
    if(!colorPicker.isType(hexStr,"String")) 
        return false;
        
    if(hexStr.length != 6) 
        return false;
        
    
    var arr = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];
    var isHex = true;
    hexStr = hexStr.toUpperCase();
    for(var i=0,len = hexStr.length;i<len;i++){
            if(this.arrayIndexOf(arr,hexStr[i]) == false){
                    isHex = false;
                    break;
                }
        }

    return isHex;
}


colorPicker.isShortHex =  function(hexStr){
    if(!hexStr)
        return false;
        
    if(!colorPicker.isType(hexStr,"String")) 
        return false;
        
    
    if(hexStr.length != 3) 
        return false;
        
    var arr = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];
    var isShortHex = true;
    hexStr = hexStr.toUpperCase();
    for(var i=0,len = hexStr.length;i<len;i++){
        if(colorPicker.arrayIndexOf(arr,hexStr[i]) == false){
            isShortHex = false;
            break;
        }
    }
        
    return isShortHex;
}

colorPicker.isHsb =  function(hsbArr){
    if(!hsbArr)
        return false;
        
    if(!colorPicker.isType(hsbArr,"Array")) 
        return false;
        
    if(hsbArr.length!=4)   
        return false;   
        
    if(hsbArr[3] !="hsb") return false;
    if(hsbArr[0]>360 || hsbArr[0]<0) return false;
    if(hsbArr[1]>100 || hsbArr[1]<0) return false;
    if(hsbArr[2]>100 || hsbArr[2]<0) return false;

    
    return true;
}

colorPicker.parseRgb =  function(inputValue){
    return inputValue;
}

colorPicker.parseLargeRgb =  function(inputValue){
    var arr = [inputValue[0]/255,inputValue[1]/255,inputValue[2]/255];
    return arr;
}

colorPicker.parseHex =  function(inputValue){
    return colorPicker.HexToRgb("0x"+inputValue);
}

colorPicker.parseShortHex =  function(inputValue){
    inputValue = inputValue.toUpperCase();
    var hex = "0x"+inputValue[0].toString() + inputValue[0].toString() 
                            +inputValue[1].toString() +inputValue[1].toString()
                                +inputValue[2].toString() +inputValue[2].toString();
    return colorPicker.HexToRgb(hex);
}

colorPicker.parseHsb =  function(inputValue){
    var hsb = [inputValue[0],inputValue[1],inputValue[2]];
    return colorPicker.parseLargeRgb(colorPicker.HsbToRgb (hsb));
}

colorPicker.arrayIndexOf = function(arr,str){
    for(var i=0,len = arr.length;i<len;i++){
        if(arr[i] == str)
            return true;
    }
    return false;
}
    
colorPicker.arraysEqual = function(a, b) {
    if (a === b) return true;
    if (a == null || b == null) return false;
    if (a.length != b.length) return false;

    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false;
    }

    return true;
}

colorPicker.prototype.initSetting = function(){
    this.img ="\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x01\x04\x00\x00\x01\x04\b\x06\x00\x00\x00\u00CE\bJ\n\x00\x00\x00\tpHYs\x00\x00\x00\x01\x00\x00\x00\x01\x018\"\u00F4@\x00\x00\x00$zTXtCreator\x00\x00\b\u0099sL\u00C9OJUpL+I-RpMKKM.)\x06\x00Az\x06\u00CEjz\x15\u00C5\x00\x00 \x00IDATx\u009C\u00EC\u00BD{\u00BCeWU&\u00FA\u008D\u00B9\u00F6>\u00E7T*\x02I\u0088\x04A\u00D4hLT\u00BC\b-\bb\x0B\x11[\u00EDn\x04\u00FBj\u00BC\u00F8k\x105\u00B7\u00EDn\x1Ft\u00B7^_\r\u00AD\x17\u00C5\u00A6\x01\u00DB\u009F\u00B7\u00B1\x1BE\u00BD`@\x1E\"\u00D7\u0080\u00A1\u0095\u0097B\b\x10\f\x1D\b\t\x18\b\x04\b\u00AF<I\u00AAR\u008Fs\u00CE\u00DEk\u008E\u00FB\u00C7\x18\u00DF\x18c\u00EDS\u0095\u00A4\u008A$\u0095\u0084\u009ApR{\u00EF\u00B5\u00D6\\s\u00CD5\u00C7\x18\u00DFxN\u00C1\u00F1v\u00AFn\u00AA\u00FBw\u00A3\u00DF\u00F8\u00B5\u0090\u008F}\r\u00B6\u00AE}\u00D8\u00E6\u0081[\x1E\u00D2\u00C6\x1B\x1F\u00DC\u00967\u009E\u00A6\u00BA\u00E7T\u00F4}\u00A7\u00E88\u009C\u008C>\u00DEO\u00C6[\x06\u00E8\u008D\u0090\u00C5-h\u008B\u00BD\u0090\u00BE\x1F2\u008E\u00806\x00\x02@\x01\b\u00BA\u009C\x00\u009D\u00ED\u0086\u00B6\x07`9<\x10\u0083\u00EC\x1E\u0081\u008D\u00BD\u00E3\u00AC\x7F\u00B1\u00C9\u00EE\u009B\u00BA|\u00C5\r\u00D2\u00BE\u00F2Zi\u00A7~a~\u00E2\u00FD>'\u00F7\u00FB\u00AAk0\u00FF\u00FAO\u008F\u00FA\u00D0O\u00CD\u0086]\u00FB\u008F\u00F1\u0094\x1Co_B\u0093c=\u0080\u00E3\u00ED\u008E\u00B5\u00DE?\u00F5 \x197\u00BFu\\^\u00FE\u00F0\u00BE\u00F5\u00C1o\u0092\u00C5\u00CDg\u00E9\u00F2\u008630\u00DEx\u009An\u00DE \u00C3\u00E2F\u00A8\u00DE\x02\u00C56\u00A0\u008A\u00D6gh\u00AA\x10\u0085\u00D19:\u00A0\x02h\x07\x7F\x12\x00\u00D2\u00FD\u008B\u008A\u00FD\u00A2\u0080\u00EA\b\u0085@\u00B4\x01\u00BDC\u00BA\x02*PQ(\x06@;D\u00ED\u00B2\u00A5\u00AE\x01r?\u00C8\u00FCd\f\u00B3Su\u00B9~\u00EA\u00B5m~\u00D2U2\u00BF\u00FF\u0095\u008B\u00FB\u009F\u00F9\x0F\u00EB\x0Fx\u00CC\x152?\u00E5r\u0091\u00AF\u00B9\u00EEX\u00CD\u00DD\u00F1v\u00C7\u00DBq\u0086p\x0Fl\u00AA\u00B7<P\u00F5\u00EF\x1F+\x07\u00AEy\u00F4x\u00F0#\u00DF\u00DE\u00FB\x17\x1E%\u00DB\u009F9M\x16\u009F\u00C4\u00B8\u00BC\x193l\x03\u00B2\u0084\u008Cpb\u00E7kT@\u00C4\u0088\u00BB7\u0088*T\u009DrU\u00ED\u00AC`\x00~\u009D\u00AA\u00FDu\x01\u00A4\u00F9\u00B5\u00A3\u00FD\x16\x03r\u00F4\u00D0\u00F3^\u00DD\u00FBU\x00\u00AD[\u00BF\x12\u00C7\x05\u00A36\u008Ch\u00C0\u00B0\x1B\u00BA\u00FB\u00EB\u00D0\u00D6\u00BE\u00E1\u00DA\u00E5\u00C6)\u0097\u00CEv\u009D\u00FE\u00FE\u00F9i\u00DFr\t6\u00BE\u00FDb\u0091\u00AF\u00BC\u00F1\u00AE\u009E\u00CB\u00E3\u00ED\u00C8\u00DAq\u0086p\x0Fh\u00AAW?\f\u00CB\x1B\u009F\u00B8\u00DC|\u00F3\x13d\u00F3\x0B\u00DF\u00D5\u00C7\u008F\u009E\u00A1\u008B\u00ABd\u00B6}=\x1A\u0096\x00:\u00D0;T\x05]\x14\u00A2\x02\u0081K\u00FF.\u00DE\u0087N\u00FA\u0094n\u008C\u00A1\u00BB4\x07\x0Fk\u00B3\u00EFPC\tD\x07\n'x\x04\x03a\u00BF\u0093\u00EB\x01h\u00CF\u00AF\u00810\u00BA3\rm\u00CE|$\x18\u008F\u00F5!q\u00ED8;\t\u00F8\u008A\u00B3\u00B4\u00AF\u009Fq\x15v=\u00F8\u00A2\u00F9\u0083\u00FF\u00F1;\u00E5\u00FEg\u00BDC\u00DA\u00E9\u00D7\u00DCi\u0093z\u00BC\x1DU;\u00CE\x10\u008EAS\u00DD{\u00A2\u00F6\x0F=i{\u00EB\u00B2\u00EF\u009Fm_\u00FA\u00BD\u00D8\u00FE\u00F8\x19}\u00FB\u0083\x10=\x00\u00C1\x12\u00AD\u00CF\u00A1:\u00A2\x05\u00B1\x1ACX\x11\u00D2.\u0099\x1D\x11\u00A8B\u00A5R- *\u00E8\u00DD~\x13%\x1Ahf-P\u00A3V\u00FB]\u0092\th^\x0F\x15\u00A8vg\"\u00C9\x15\u00B4\u00E7m\u00D0\u00ADoU\u00C4qh2\u00A4`4\u0093\u008B\x00\u008C\u00E2\u00C0\u00C4m\x16\u00BB\u00BF\t\u00FD\x01_\x7F\u0095\u00EC\u00FE\u00A6\u00B7\u00AD?\u00F8\u00F1o\u00D6]\u008Fz{\u009B\u009D\u00BC\u00EFN\u0099\u00F0\u00E3\u00ED\x0E\u00B7\u00E3\f\u00E1nj\u00DA?\u00FD\u00D0qq\u00D9Se\u00EB\u00A2\u00A7\u00E8\u00F8\u0091'\u008C[\u0097\u00AF\u008B~\x1Es\u00C2\u00F3\x15\t\u008F.P'(C\x02\u00C6\x10H\u00D7\x00\u00D0\u00D4\u00E8L\x0Eq\u00B9\u00DD\u00B4\x15\u00E9,\u00E8\u00DD\u0098\u00CC\u00E4\u00FC\u00EE\u008B@\x04\u00E8\u0085!t^\u00DF\u00ED\x18\x00\u00ED\u008A\u00D6\u00A7\u00F72\x1E\u00D4\u00FC\x11\x12\u008D\u0088#\u0097\u00E8\u00B32\x1Ax\x1F\u00CA\x05(\u0086:D\u00A0P\u008Cr\x1A\u00DA\u00C9\u008F\u00D8\u00D2\u00DD\u00DF\u00F8N|\u00E5\u00B7\u00BDq\u00FE\u00A0\u00EF~\u0083\u00B4o\u00F8\u00EC\u0091\u00CF\u00FA\u00F1v\u00A4\u00ED8C\u00B8\x0B\u009B\u00EA\u00E7\x1F\u00A6\u00E3\u00C5\u00E7\u00F4}\x17\u009E#\u00FD\u0083\u008F\u00D1\u00ED\u00F7\u008B\u00E8\x01\u0093\u00EC\nhs8n'\x17\u00E3\x1E\u0092\u0088*J \u00BD\u00FA\u00BFBf\u00A1I`\u00D3\x01\x14\u0086\u0082f\u00EACg\x07\u0085`A\x04\u0081r\x03\u00DA\"4\f\u0090\u00DAs\u00BC\x13\x06\u00B4\u008A0t\u00B5?8\u0083\tS\u00A6\u00DB6\u00AA:\"9|\x15H\x17ho\x18e\x06}\u00C0\u00B7h\x7F\u00C0#\u00FE~v\u00D2\u00E3^'\x0Fy\u00FC\u00EB\u0086\u00F97\x1DW-\u00EE\u00A2v\u009C!\u00DC\u00C9M\u00F5\u00B3'a\u00FC\u00F0\u008F\u00EA\u00E6\u00DB\u009F\u00DE\x17\u0097<\x1E[\u0097\u0089\u00C8^4\b:\x16\u00C1\fr\u00F5K\u00BD\x18\u00E8-\u00A9-\u0088L\u00F28\x7Fr\x04\u00DE\u0082Z\u0091\u00A8\x02T\x07\u00F8\u00D1\x11\u0082\u00A3\x05\u00A2\x06\u0081\u00A2Ol\x11\u00E2\u00E8\u0081\u00D7\u00B6D/ac\u00D0\u00B0E\x06_\u00F1c\u00D5\u00D6`*\x03\u009F\u00CB\u00FB\u00D7\x0E\u00A09\u00CFQc\x01\u009C\x0F\x01\u00B4O\u00E7B|.\u0094\fMf\x18\u00BF\u00E2\u00E1:\u009C\u00F4\u00A8w\u00E3k\u00BE\u00F7\u0095\u00C3\u00C9\u008F\u00FAs\x19\u00CE\u00B8\u00F9(_\u00D5\u00F1v\u0088v\u009C!\u00DC\tm\u00B9\u00D8\u00D7\x06\\\u00F6\u00A4\u00AE\x17\u009E\u00DB\u00B7/~j\u00DBz\u00D7\u0086\u00F4\u00BD\x10\x1D\u00FD\u008CJP(\x04\u00CC/~\u0096\x16\u00A3\\X\u00FC\x10\u00C4j\x12Z\x0F\u00CF\x10T\x12\u00E6\x17\u00B8@\u00C2\u00A2\u00B4\u00AF\u00E8\u00A2\u00A3\x10\u00B28C\x18\x15\u0082\x06\u00A0%3Pu\u0095@\u00CAE\u00C0\u00A8H5$\u00DC\u0096\u008A6J\x1D\u0082\u00DD{\u0082*\u00A8\na\u00C2\x10\u00BAx\x7F\u00BD2GI\u00A68\u008EP\x05\u00C6a7\u0096\x0F|\u00DC&N\u00F9\u008E7\u00AC\x7F\u00ED\u00D9\x7F\"\u00BB\x1F\u00FFv\x19v\u00AD\x18*\u008E\u00B7#m\u00C7\x19\u00C2\u0097\u00D0T\u00AF:\u00B5/\u00DE\u00FFS\u00BAx\u00C7O\u00CB\u00E2\x1D\u00A7k\u00FF\x14\u009A*\u0080\u00D1\u008CpA\x01+\f\x01HB\u00A8\f\u00A1\u00FC^\u00ED\u0083\u008C\x0F q\u00AA\u00EB\u00E5\u00EA\u00E7E\x0Fa\u00C9\u009F\u00EA\x0ER\u008D{\u00D0T/(\u00DE\u00DD0!$\u00BC\u009E\u00D0~\u00CA\x10\u00EC\u00B3t\u0089\x01\u00F6^\u00EF\u00EF\x06F\x1F\x1F\u008D\u008D\x02^o\u00F7\u0094 \u00F2|&\x1Bw\u00CE\x07c\x1Fv\u00B8H\u00F9\u00EC\u00BD\u00C5\u00B9\u00DBk\x0F\u0081<\u00F8\u00B1W\u00B7\x07=\u00F6\u00A5\u00B3\u00D3\x7F\u00F8\u00FF\x15\u00F9\u009A\x1Bn\u00E7\u00D5\x1Do\u0087i\u00C7\x19\u00C2Q\u00B4\u00BE\u00FC\u00C0#\u00D1/|\u0096.\u00DE\u00F64]\u00BC{\u00A3\u00F5\u00BD\x10\u008CyB\u00D1\u00A5\u00A7\x17\x02\u0080\x13\x1C!\u00F5\u00EA\x1B\u00A8\u00C4\x1A\u00FC\u00A4X\x12\x01wA\u00DA\u00EF\u00AA\u008A\u00A6\u00D4\u00CB{\u00DC~\u0095\u00A1H\x10\u00BA\u008D\u0083h$\"\x18h_\u00D06}\u0086\x18SJx\x19WPM\x19\u00BBT\u00A3\u00A3\x1B5\u009B\u00E6\u00B9\u0081VB\r\u00EAq\u00ADj2\u00AFP[\u00F8\u00FC\x00z\u00D7psNb*`\u00F6\u0086e\u00DB\u0080\u009E\u00F4\u00E8M<\u00F81\u00AFY\u00FB\u00FA\x7F\u00F1\u00DF\u00E4+\x1E\u00F7\x01\x1CoG\u00D4\u008E3\u0084;\u00D8T\u00AF\x17\u00E8U?0.\u00DE\u00F8K\u00B2x\u00D7\u00D9\u00B2|?\x04K\u00A4B\u008D)\u00D1R\x02\u00F6\x02\x0B\u00C8\x10&F8\u00CD\u00F3\u00E3^\u00E9\x19\u0098\u00A0\n\x12\u00D2\nC\x00\u00DC\u0095\u0088\x1E\u009E\u0087\x1D\u00CC\b\b\u00E9\u00CE\u00A0\"\u00C2|\t\u00C9\u00EF\u00E3\u0090\x12\u00B0\x14\u0092\u00DC\u00A5}Hz\u009D0\x1D%R\u00E0e\n\u00A0\u00B7\x15\u00C2V\u008F~\u009C\"\x04u\u00DD\u00C5\u00AE\u0097\x00TfG\u00F0>\u0091\u00F7\u0097\u008E\u00F0\u00AELl1*\u00E8\x1E\u0086\u00BD}\u00E27b8\u00ED\u00BB\u00FF\u00AE\u009D\u00FE/^4<\u00F0\u00B1\x7F#\u00ED~\u0087\u009A\u0091\u00E3m\u00A5\x1Dg\b\u00B7\u00D3Tu\u00D0\u00E5[\x7Fd\u00EC\x17\u00FC\u009A.\u00FE\u00FA\x11\u00D2?\u0085\x06E\u00EB\u0087\u0080\x00U\u00AA\x020\u0082.Q<\x13\u0098\u008E\t\u00EC\u008F\u00F3\u00E1\f\u00A1\u00C2w\u0091 \x10\u00B3\u00C0\x17\u0086\u00D0\u008D!Xs\u0086\u00B0\u00AA\u009E\u0094\u00E1I7\u00D7^\u00C4\x0F\u0088\x13\x1D\x10\u00FF\u00D2\u00E2OF\x116\fu\x1BF\u00C0w\u00F7Hja\b(\fI\x0BC\x0B&\u0092\f&\x18\u0082O\u0090I})\u00FCU\u008B'%\u00BD/\u00DA\x01\x14\u00B4\u0090\x0F\u00E8j\u0084\x02\x18\u00E7PUl\u009Ep\n\u00F0\u00E0\u00EF\u00B9l\u00FEuO~\u00FE\u00EC\u00C1\u00FF\u00E4/d8\u00A5@\u00B9\u00E3m\u00B5\x1Dg\b\u0087i\u00AA\u00D7\x0F\u00D0\x0F?M\x17\u00AF\x7FN_\u00BE\u00F1\u00AC\u00A1_\x0B\u00C8\u00B6/H\u0087\u00E0h\x00z\x12+\u00CA\u0084V=~\x07\u00E4OF\u00D0\x1D\u00FE\u008AN\u00AE\u009E\u00C2k0\u008F\u00C0m\x06\u00BD\x10g\u0091\u00DE\u00ADx!v\u00A8,D\x1C\x107\u00D8\u00F9u\x18w\u00BA\x12\x15\x19i\u00B8\u00D4\u00B8n\u00E2\u00FA\u00F4F\u00F5\u0081\u00AAC\u00C48L\u0090\u008D\u00DB\x1DbNJ|D\x18:]\u00B5\u00D0\x16@H\u00DCf\x10h\u00896\x14\u00F5\u00F9%##\u00E3\u0099\u00DC3U.\u00ED\u00C0b~\n\u00F0\u0090\u00B3\u00AF\x1C\u00BE\u00FE\u009F?o\u00F8\u00EA\x1F|\u008D\u00C8\u00C9\u00C7\x19\u00C3!\u00DAq\u0086\u00B0\u00D2TU\u00FA\u00F8\u008E\x1F\u00D6\u00F1\u0082\u00E7\x02\u00AF\u00FDf\x19o@\u00EB\u00DB)\u00C6}\u00D1\u0086qK\u00A6D\u00DC\u0082Z\x14\x13\u00A3Xa\b\n\x14(\\\u00AC\u00ED\u00AB\u00AF\u00A3\u00B8(\u00ABI\u0081\u00C4%+\f \rx\u00E5F;`u\u00D1\u00F3\u00CB\u00B8\u00C4\u00EFA\u00DBF\u00C0w\x12\u00F1\x04\u00D9PUQg\b9\u008E\n\u00E3\u0095\u00FF\u00E9S7\u00A4B\u0092\u00D0\u00E3\u00F9z\u00AA,Z\u0080E\u00D7\u00F0b\u00D49RG(D:\u0093\u00A0\u00AA\x1A}\u00C9\u0081i\x03\u00C6\u008E\u00ED\u00F9\x03\u00D0\x1F\u00F6O?2\u00FF\u00FA\x1F\u00FC\u008D~\u00EA\u00F7\u00BD~m\u00D7)\u00C7U\u0089\u00D2\u008E3\u0084\u00D2\u00C6\u00F1\u00D2\u00B3u|\u00D5\x0B\u00B4\u00BF\u00E1\u00D1\rW\u00FB\u00E2\x1E'\u00BA\u00B2-22\x04\u0087\u00CFe\u0091N\x18\x02m\x00\u00AB\fA\u009C!t\u00B1K\u008B\u00B4\u009B\u00BC\x12^W \u00B3\u00B2\u0083\u00C2\x10\u00E0\u00A7u]\u00F5: u\u00ED\u00C9\u0083\u008A\u008F\u00DD\u00CE\u00A9\u00C6;\t\u00D5\x00\u00888\u0084\u00A2\u00E2\u0098+\x12\u0080v'J\u0089\u0090\u00A2\u00C9\u00BD\u009COtOz\u0092\x12c@\x04!\u00D5\u00CB\u0081>\u0091\u00F0]\u00BD\u00D7C1\x04\u00D1D!=\x19\u00E4\x04\u00BA\u00F4\u00B6\u00C2\x10`>RiP\fX\u00CENA\u00FF\u00EA\u00EF\u00BF\x04\u00DFr\u00CE\u00AFl<\u00E8\u00C9\x7F\u0087\u00E3\r\u00C0q\u0086\x00\x00\u00E8\u00FA\u00E13\u00FB\u00F8W\u00BF\u0083\u00EDW=\u00B9\rW\x01\u00D8\u0086\u00D0\u00FAM\x1D\u00BEH\u00D2$\x14\u0093`\u00D5\x18\x16m\u0095\bU\u00A0\u00BDO\u0088\x15\u00B1\u00C8W~\u00B3;\u00D9OaX+\x04\x15\u0092\u00BE\x01\u00BA\u00F4\u00F3\u008C \u00D3\u00A2O\u0082\u00F3{\u00D7\x1B\x15\u00EB?\x01\u00800\nq\x15U\u00D0\u00C0X=\x10\u00B4a\u00DC\u00CE\u00B3P\u00B5\u0089\u009F\x14h+\u00AAC\x18]\u00FBT\u00AD\u00D22\u009F\u0093x\x05m\u00C6a\u00C2\u00FD\u0088dj<\u00DE\u0089X\u008C\u00D9H\u00A7\u00E7\x04\u00C1\u00C8\u00CC%\u00DA\u00B0\u00BD\u00FBk\u00A0_}\u00F6\x05\x1B\x0F\u00FF\u00F1\u00FFKN~\u00C2G\u00F1e\u00DE\u00BE\u00AC\x19\u0082\u00EA\u00A7\u00EE?\u008Eo\u00FB\r\u00ED\u00AF\u00FD\u00B9\u0086w\u00CE\x1B:\u0080\x11\x1A\u00BEr$\u00E1\u00C3\u008Cq\u0093\u00EC@\u0097\u00E0i\u00F4\u0092\u00C95\u00AB\fbjq/\u00FFR\bWi]\u00FB\u00AA\u00F7\u008C\u00B1\u008B\u00DF\u0097H\x05\u00A0:0\u0085\u00CE\u00DEw\u00B9\u0098\f\u00A5Q(ke\b+\u00CF0\u0089E :\u00C8\u0098\x04E\u00BA\nWU\u0098I\u00CE\u0084\x16\u0086\u0080\u00D2\x1F\x19\x02\u009F\u00BF\u00CC\x17m#S\u0082'\u00F4\u00D0\u00B8\x06\u00F5\u009C\u00BE\u00C2\x10 \u00AE\u00D6\u00D0k\u00D3\x11\x01Vj\u0091\u009D#\u0080\u00F1\u00E4oY\u00C8\u00D7\u00FD\u00E0\u00EF\u00AF}\u00EB\u008F?W\u00D6\u00CF\u00DC\u0083/\u00D3\u00F6e\u00C9\x10\u00C6qK\u009A\u00BE\u00ED\x19\x1D\x7F\u00FE\u00C2\u00AEox\u00D0L\u00F6B\u00A5\u00E7\"\u00EE2\u00CD1\x00\u00A0\u00AD\u00A1\u00FB\u00E2m!\x11\u009DI8\u0094\x15\x14\t*\u0098\x12\u00BF\x1B\x00W\u00E1;\u00D5\x07\x1DI\\\u00F6\u00BB\x14D\u0090\x12\u00D8\x16\u00B8\rK]\u00EFoa|c\u00EB\x0E\u00C7\x05\u0088 \u00A1\t3q\u0084\u00DE\u00BA\u00A07u\u00C8\r\u0084[\x10\u0080\u00D6\u00EC\u00C4\u009A\x13\u00A1\u0080\u00A2\x01}\u00CCHG\u0097\u00EE4\u00A70@s5\u0089\u008A\f!\u00DC\u008C\u0095!`\u00FA\u00FC6^\u0081\u008EN\u00D4\u0095\u00A1\x16\u00DBF\f\u0093Lal\u0089J\u0098\u00A7\u00C1\u00CC\u00CB`\bD\b\u00D3\u00F7\u00B1\u00A5s\u00E8iO\u00BC\u00AE\u009Du\u00CE/\u00EB\u00D7\u00FE\u00D0+66N]y[\u00F7\u00FD\u00F6e\u00C7\x10\u00B4_qV\u00D7\u00BFx\t\u00FA+\u009E\u0088\u00E1\u0093\u0096[P\u00CD\u00EC\u00AB\b .\u00AC\x04M\u00A3\u0095QVD\u00F0\u00D5\u00A0\x1E\u0094\u00EB\x15\x0E\u00BD\u00D9w1pS\x1D \u0095\u00C6}\x0FaK\u0088\u00CF2\u00FDL\u0094P\u00A5\u00AA\u009FP\u00DDv\t\u00DF\u0093@\u00A4{\x05%WM\u00EATD\u00CA4\u00C7\u00C2\u0080*\u00B1\u00AAI\u00F0k\u00D9\u00A8\u00DA\u00D4\u00FBs\u00CE\u0098CAU\"]\u00B45_B\u00A6\u00CF\n\u0089\u00E8E\x16{YuA\u00C2]\u0090;T\u0096.1\u0096L\u00E8B0>\u00DE&\f\u009B\u0081Z\x06\u008C\u00EDT,N\u00FF\u00A7\u00EFX\u00FF\u00DF\u009E\u00FEo\u0087\x07=\u00E9J|\x19\u00B5/\x1B\u0086\u00A0\u00BAw\u00DE\u00FB\x1B~u\u00C4\u009F>[\u00E5]\u00EB\x03\u00B6\u00C3\x1C\u00B8#\u00E30\u008C\u0081\u00E9\u00DF?$CpW\u009C\u00C6\"n\t\u00E3\u0081)C\u00A1\x1B/\u00EE\u00B3B\u00E0\u00BCn\u00C2\u0088n\u00DB\u00C08\u00D5\u00F7ih3\u00C2\u00AB\b\u00A5\x05\u00A1\u00EEd\b\u00E1Jt\u00A9)u\f\u00D5]*\x00F\u00DE{\u0088\u00EB\u008C!\u00FA)\u00E5>\u00BC\u00FF\u00E1\x19B\u00CE\x19B\x05Xa\b\x13\u00E6\x027\u00A4\u0096\u00E7\u0087\u008F\u00A9\u0097w\x13\u0088$\x19B\u00A83\u00EC\u00BB\x0B\u00BA[U\u009A\u00A3\u008DV\u00E7s\\G\u00EFKl\u009D\u00F2\u00AD[\u00C3YO\u00FB\u00ED\u00B53\u00FF\u00E5\x7F\u0091\x13\u00BFz\u0081/\u0083\u00F6e\u00C1\x10T/}\u00E4v\x7F\u00F9\u00CB\u00C6\u00FE\u00DAG\u00CCf7`\x00Cy:F\u00E4\u0082\x05\x17q\r\u00ACA\u00EA\u00C6\u0094\u0096\u00D5\x16\u00C0\f=\u00E1a^3\u0089C\u00C0\u00E4z\u0088CeU\u00FBL\u00C8\u00DF\u00F34\u00A9\u00D7y\u00F4\u00A0z\u0090S\u00B5\u00D8\x1Bn\u0097B\u00D4\u0085\u00DFh\u009E2\u00D1\u00C5\u00EB\u00DC\u0090\u0089\x157\u00E0\u00A4\u00C2\x11k\x1C\u0084-A\x10\u00B6\x04\u009B\u0080D'%8\u00A9\u00D7{\u00D5\u00FB\x1F\u00C2\u00E3\u00C0\u00FB'\u0093J\u0086Y\u00F3\x1Bb\u00DE\u00BB\u00E4\u00C2-\f!\u00CE\x1B=\u009C\u00DB\u009F\u00C5\u0090P\u00CE\u0087M\u00A33\u00A7C1\u0084\u00CE\u00C7j\u00E8\u00B2\x0B\u008B\u00D3\u00FF\u00D9e\u00B3o>\u00E7'\u00D7\u00BE\u00EEG\u00EF\u00F3\u00A1\u00D0\u00F7i\u00860\u00EA\u00FE\u00D9\u00F6\u00F2u\u00FFq\u00D9^\u00F6\x1C\u0095w\u00CF\u009B\u008C\u0098\x01\u0098\u00C1\x18\u0082\u00D0\u00D4\u00A6\f4*R\u00A4\"\x05\u00B6j4\u00F3\u0096\u00C4\u00E6zt@\u00D0\u00DB20\u00D6E_\u00EE;1\b\u00A2\u00F4#y\u0080\x16\x7F\u00F6\x13\u00FD!\u00FA\\5\u00E4\u00ED0\u0084N\f\n-$(\x0F\u00D1\u00F7_\u0087/\x15\u0095T\u00AF\u0087GlV\x064E.+\u00DF\u00CB_\u00CD\u00AF\u00A8\u00E3\\5\u00BE\u00F2\x1D\u00C5\u00FD\u0082\u00D9H\x06$\u00D1\u00C8\x19\u008Cg\u0085!\u00F8\u009C\u00D7w\x156\u0099nk\x01\u00CEl8\x07\u00E8\u00E6\u00A1\u00E8\u00DA\u00B0}\u00CA7.\u00E4\u00E1?\u00FA\u00BC\u00F5\u0087\u00FF\u00EB\u00FF,\u00EB\u00A7-q\x1Fm\u00F7Y\u00860\u008E\u0097\u009F\u00B1\x1F\x7F\u00F6\u008A\x03\u00F2\u00F2\u00EFX\u00C3u\x18\f\x00`\x06\u00C5\f\x02\u00F3F{\u008B\u0090W\u00EA\u00F0\u0085\u0088\u008B\u00D4?\x1CC\x00\x1C)L\x16\u00F2a\u00EC\t\u00F0\u00FE\x0E\x15\u00FA\x1C\u00F6\x04\u009D\u00DE\u00B72\x04 u\u00E6\x18W\u00C6\x06L\x18\x02\u0092\u00D0\x12\u00A6\u00AF\u00BA \u0093Xz\x19+m\x0F;\x19\u0082\x06\u00D1\u00C4X\u0090C\x16\x7F\u008EP\x11\u00D8\u00DF\u00E1\\\u0090\u0085\u0090s.W\u0090\u008C\x02\x1Ah\ra\u00EF\b5\u008C\f\u00A1\x04rU\u00A6\x15\u00B6\x02\u00C0S\u00C8\x0BCX\u00AD\u00F9@d\u00D1\x01\x19\u00ED\u00B7\u008E\x01\u00AA\x02\u00C5.\u008Cg<\u00E5}k\u00FF\u00E8'\u009F\u00D1\u00BE\u00EA{\u00AF\u00C2}\u00B0\u00DD'\x19\u00C2\u00A6\u00FE\u00E5O\u00EC\u00D5\u0097\u00BEx\u0081\u00B7\u009F8\u0093%\x06tg\x02\u0086\fLeP\u00CC\x14hu\x06V\nt\u00C4B\u00ED\u00AB\u00D3\u00B4s\x11\u00D7H\u00BE\u0094v\u00A5\u00D8I\u00B94\u00FA\u00C7\u008A\u0095\u00BD\x10!\u00EF\u00A8\u00BD\x10c\u009C\u00A7IP\u0093\x03\u0094\u008A\x19\u00E4\u0093:|J\u00CFU>\x04PZ\u00D3X\u00DA\u0083\u00B8\u00E9\u00CB\u00AF\u00EAG\u00CC\u00D5\u00A4\b\u008AzAW/\u00D3\u00EE\x07\u00E8\x05\u0088\u00BA\u008B\n0\u0097\u0082\x1E\u0087\f=&GX\u00B5%\u00DC\x06C\x00\u0092A\u00B2\n\u00F5\u00EAs\u0086\u0097\u00E1(\x18\u00C2d^\x15\u00AA\r\u009B\x0F|\u00CC\u00BE\u00D9\u00B7\u00FD\x1F?\u00BF\u00FEm\u00FF\u00E1\u00E5;g\u00F2\u00DE\u00DD\u00EES\f\u00A1\u00EBU'\u00DE\u00DA\u00DF\u00F4\u0092\u00BD\u00F2\u00E2\u00A7\u008F\u00F8\x14\x06\u00E9\u0098A1\u00A8\u00AB\t\x02\fh\u00CE\x10z0\u0084\u00A9\u00DE[\u00F4SMC\x1DP\u00A5\u0096\x11\u0089\u00ADWM\u00F8\u00AA\u00E5\x1A\x05\u00CC\u00C8\u00A8+\u00E8 \u00FB\x06\x122\u00F3\x10m\x18S\u0086\u00E0\u00FD\u00C7y\u008A\u00ACPT\u0099\x18\u008D\u00A1=nQ\x19B\u00DC\u00B6\u008E\u00B7\\+\u008E4*\u00CC\u00D6J,X\u00C9\u00C2\f\x02\"#\u00EC\u00F1]\u00DC\u00A8\u00A2\u0085\u00C8\x15E\u00A5\u00F2\u00F1\u00F3\u0099'\u0092\x1E8D\u00A0\x12\u00C7\u0088T)\u00F8\x1C4*\u00D6|\x06\u008Em\u00C5-\x19\f\u0096\u00CF\u00B5\u00C2\u00D4\u00AA\u00CA\x10\u00C9^nS`\u009F\x1D\r\u00E3\u00DA)\u00C07=\u00E3\u0095k\u00DF\u00F5o\u00FE\u00AD\u009Cp\u00C6}\u00A6\x18\u00EC}\u0086!l\u00E9\u00DF?\u00FC\u0096\u00F1O_w\x13^q\u00D6l\u00B8\x15\r\u00C0\\\u00A9\"\x00\x03\x04\u0083\x003/f&P\u00AC)m\t\u00B0\x1A\u00A3\x00@\u00E9L?\u00FE\u008A\u00E4\u0098X\u00B2U\u00D0\u00D1\u00C3S\u00D1'\x0B\u0096\u00BA?\u00AF)\u00D4W\u00BFW\x1BE=\u00CE\\\u0087\u0090\u00CC+\u00AFj\u0087\u00DA\u00C01\u00EA\u008E{\u00A9\x1B\x02E\x07\u00C0]y\u0082\x16L\u00CDG\t\u00BA?\u00ED7~\u00F6\x10\u00E9\u00F8k;\u00EF\u00EB\f*m\x18|\u00B6\u009E\u008C\b\u00F4\x04\u00D896\u00C7\u009A\u00CF\x11:|_\u00E9\u00BF0\u0085\u00A2\u008AL\x18\x02\u0099X\u00ECSAC\u00E5j\u0090\u0092\u00A3\u00BD.9\u00CE\u0082\u00B22\u00E9+\u00E7\u00D3\x18\u0083\x04C\u00A0\u00F1R\u00D1\u00A0}\u008E\u00ED\u00AF\u00FB\u009E+7\u00BE\u00FD'\u00CE\x19N?\u00E7\u008AC\u00BC\u00C9{]\u00BBO0\u0084}\u00FD\u008D?v}\x7F\u00C9\x1F\u00ED\u0093\u00B7\u00EC\u009EA14\u00B3\x0F\u00CC\u00D4U\x03\x18Ch\x02\u00CC\u0091L`\u00EEv\u0084\u00AC\u00BDS\u00BC\x055\x18\u00C6\u008F)-\u00E4Q/\x00\x18\u00A1\u0093\x12b;\u00A5j\u00EA\u00E8\u00D1\u00AAd\x0E=\u0098\x18\x7F\u00A5@I\u00FC[\u00CE\u009F\x1Co\u00D9\x15\x17q,\u00FE9h\u00B57\x1B\u00C7\x00\u00E8,\u009F\u00CF\u00AF\x11\u00DE7\u00AE_a@\u0094\u00AE!\u00A5\u00D5C&\u0082\u00DA\u00D3\u00D7\x0Fx0T\u00CEA\x10?\f1\u00D8\u009C*DGG\x15=\u00BD*\u009C\x06g&\u00AB\u00F3\u009F^\u0088\u00A9\u00B1\u0094j\x14\x19\u0085\u00A1\u0085\x12\u00A4\u00C4k<\u0098,\x10\u00C5\u00B8\u00CA\x10\u00AA\r\u00A7\u0099g\u00A7\u00A3xr\u00CA\u00BCu@\u00BB`\u00FB\u0094o\u00DD\u008FG\u009F\u00FB\u00AF6\x1E\u00F5\u00F3\u00AF\u00C6\u00BD\u00BC\u00DD\u00AB\x19\u00C2Ro\x1En\u00EE\u00AFz\u00E1u\u00FD\x0F~a{v\x15\x1A\u00B6ME\u0080\x18C\u0080\u00BA\u00CD@\\= s\u00B0s\x1A\x14s\u00FF\u0097d\x15\f\u00A1 \x04JPJ\x0B4\u0087\u00FA\u00AAQ^\u00B02\x04(-\u00F6\\\u00B8\u00AB\u00F0\u00BC|\x16\u00B8>~\x1B\f\u0081\u00D7P\"\u00FAw\x052N\x1F\u00CD\t\u00BE\x19\u00E1\u0088B\u00FAZH}\u00AA0\u00CCc\u00A8\x03\u0091p%\u0096sw\u008CYr\fa|M\u00E1n\x04R~<\u00DC\u00F8u4\"#aw\u00AF\u00D5\b\u00B7\u00F4w\u0086\u008F\u008F\u00A0\u00CA!\u00D0\u00A9[\u00D7\u00FB]\rR\u00EA\u00BD\x04b9\x11O\x1E\u00D5\u0089\u00B8\u00FB\u00FC\u00EEd\b\u0098x,\u00A6\bA\u00C1P\u00E7\f\u00F62T\u00A2\x10,\u00D7O\u0083~\u00FB\u00D3\x7Fw\u00ED\x1F?\u00EB\u0097ex\u00E8\u00BD6\u00B5\u00FA^\u00CB\x10\x0E\u00EA'O\u00BA\u00BE\u00BF\u00EC5\u00D7\u00EB\x7F\u00FF\u00BE\u00DE\u00F6\u00B8}\u00A0\u00FB\u009F`\x0E\u00C1\u00CC%\u00D1\x00\u00C1\f\u0082A\u00D4\u0098\u0084\u00C2\u00D5\x07\t\u00840\x14\u00A6p(\u00E2c\u00E8\u00AD\u00D9\x05\u00AA\u0095\u00DE\u00FF\r]\u00B9\u00F8\u00E8A\x1D\u00FA0(A\u00F3\x16v\u008F\u0095\u00E3E\u008FN\u00D4\u00C2\u00E2\u00A7\u00FC\u00D7\u00EEi\u00F9\tm\u0082\x00Z$#\u00B5\u00F2<\u00AD\u00D4\\\u00A4-\u00C0\u00F5\u00FE\x18\u00BF\u0096gpc\u009C3\u008B\u0088\u00AF\u00A0z\x00E\u00A6\u0083\u00AF$H\u00A1F\x17\u00F22\u008D\u00E8H\x06\x1A1\u0095\x1APHw\u008F^\x1F\u00BD\u00EF\x0EQ\u00CB?P\u00EF\u00AFi\u0086N\u00C7s\u0089\u0098\u00CA0\u00F6\t\u00D2\u00C8\u00B9l\u008E \u00EC\u00DA(#\x07\u0084=\u00C3\u00C2\u00B0Q\x10\x06\x19\u00BFLl!\u0093\u00EC\u00CA\u00D2\u00C7\x12\u00BB\u00B1x\u00F8\u008F\u00BCe\u00E3;\u00FF\u00DD\u00D3\u00DAW>\u00EA^Y\r\u00FA^\u00C9\x106\u00F5\u00B2o\u00F8\u00F4\u00F8\u00E2\x0B>\u0083W\u009E9o\u00DB\u0098\u00A1\x192\x10\r\u00860\u0080\u00EA\u0081\x19\x15\x07\x11\fP\u00FB\u00CD\x19\u0082\u009D\u00A7\u00E9\u0081P\u00844\u009A2\u0084b`(\u008B\u00DB\u008E\x15\t\u00C8s\u00AB\u00F4\u009C\x14,\x05d\u0095)H\u00E9s\u00D5\u009E@F\u00E4\u00A3\u00B2HB\u00C38F\u00B8\u00CD\u0089}\u00F0\u00FE\u00E9G\u00A1E\u00DF\x18\u00C2\u0084p*\x13@\u00F3b\u00A5q\u00A3x\u00D6\u0088\u00CB\x00b\u00EC\u00D3}\"\u008DP\u00BA\u00DF\u00C7\u00A4\u00B2)\u00F1B\u00D8@&\u00B7\x03)\u0088\u00A3\u008C\u00D1\u00C7\u0099\u00D7di4\u00BAPml\u00A2#\x14\x0B\u00A8v\u00B4b\u00A7\u0090\u0092\u00DF\u0081\x11\x11\u00BCU\u00FFjH\u00B5\u00A83\u008E\u0089\u00AD\u00E30\fa\x15!\u00F8\u00FB\u00AD\f!\x12\u00DE:\u00A0\u00E3\u0080\x0E\u00C1\u00F2\u00F4\x1F\u00F8\u00E8\u00FAc\u00FF\u00CF'\u00CB\x19?\u00F4q\u00DC\u00CB\u00DA\u00BD\u008E!\u00EC\u00D1\u008B\u00BE\u00EB\u00EA\u00E5\u00EF\u009E\x7F\u00ED\u00F0\u00A6S\x06,\u00B0\u00AE\u00DD<\x07\u008D\u00AA\u0080\u00D9\x05\u00A6\x7Fb\u00AA\u0082*f\u0082\u00C2 \u00D2\x0598S\x18T\u00D1\\0\u0087\x1E\x1D\u0095\u008B\u00FCo\u00E2\u00EE#\u00E5\u00AFD0N\u00A2\u00FE\u00EC8\u00ED\x12\x13\u00A4P\u0098L\u00E8\u00C0q\u00C8\u0089^\x07'\u00FA6\u00FDW\u00C5\u00C7F\u0086\x01\x7F\u009A\u00FCl\u00E3ru\u00A0,^2\u0090\u0090\u00D4\x00\x18\u0085\x18F\u00D1\t!3\u00BEA1\u0089\u00E0,\u00EED\u00D0\x00\t>\u00B7&\u00DA\u0098\x140\u00D1\u00F2\x07\u00D4B\u00AB\u0093\u00DF\u00B4g\u00BC\u0086.m\u00EA\u00B5\u00A31\\\u00DA\u00D1\x03\u009Ci\u00A0\u00AB\u00AB#\u00F9\u00FEt\u00A4\x1A\u00C1\u00F7\u00E3hf\x05!\u00A4\n\u0086\u009D\f\u0081\b\u00C1\u0099\u00D9\u00E1\x18\x02T\u00D0G`\u00D4\u0086\u00E5\u0083\x1FyS{\u00DCO\u00FE\u00D0\u00C6#~\u00F6\u00A2\u009D\u00AB\u00F8\u009E\u00DB\u00EEU\f\u00E1\u00DA\u00F1/\u00CE\u00B9\u00BA\u00BF\u00F8\u00BC\x1B\u0087wm4\u0098+q\r\u00DD\u0088\u00BC\u00D8\x04\u008C\u00D0i/\x00\x06m\x18\u00C4T\x05C\x12<\u00D7Hf\u00A6\u0082A\x1A\x1Az\u00D8\x1381]=\u00DE\u009DR\r\u00C8p\u00E5\x15\u00A4P\u00E9<\u00F7L\\\u0085\u0096\u0094\u00C8\u00BC.\u00AEp}\u00BA!\u00CA\u00B3\x05\u00F1\u00CF\u00CBg\u00AA\n\u0082\u00D4\u009F\u00AB\u00E5\u00BF\u00D4\x15\fd\u00D0\x10~\u00FC\x12\u00A0d\u00D7\u00A69\u00B5\u00C6\x1Cd\u00E0\x0F\t\u0099\u00CFa\f\u0083?5'\u008A\u0089\u00A4\u00AFAW\x135bu.4%\u00B5\u00C7.h\t\x0E\u00CB\u00A4$'>\x1D\u00C1\u00BD.\u00B42\x03\u00ED\u00E8:\u00A2\u00C5\x1E\u0098Z\u0090\t\u00E7\u0096]\u00B14\u009D\u00A1\x1B\u00E9c\x18\x18\u00A5\u00BA\u0081i\u0083`\u00DC\x02]\u008F\u009C\u00C3\u008A|z\u00F6\u00CFu\u00A0\u00E3\u0080\u00C5Ign\u00EAw\u00FE\u00C4\u008F\u00EFz\u00DC/\u00BF\x0E\u00F7\u0092v\u00AFa\b\u00D7\u008D\u00E7\u00FD\u00FCe\u00FA_\x7Fo\u00DFpEk\x1Ehd\u0084\u00DF=\x1C9\u0089<U\x01`-\u00BE\x1B\x01O\u008C\u008D\u00C2\u00EFfc\u0090@\x17I\u00B0\u00944\u00A1F(\u008B\u008C\u00DA\u00F7i\u009A4\u00F5\u00ED\u0095\u00BA\t<\u00EE\x04\u0095\u00E7\u00B3Q\u00A5\x10\u00A0\u00CF\x00]+\u00A8\u0080\u00CC\x01 #\u00982\u008DrmA\x05S\u00D8,\u00B9\u0098\u00D1V\x02\u00B0\u00CA\u00FD\u00B9\u0093tx\tVT\x0E\u008F3PZ\u00F8V\u009E\u009Fi\u00D7\x13\x04\u00E0\u00C7\u00CC`\u00A8\u00D1\x7F\u0086\n;S\b\u00B5e\u008C\u00DF'\u00E1\u00D2N\u00F8\u00DCNN1B\u00B0\u00F49\u00EB\u00CE,\u00C8 Fg\f\u00FC~\u00FB\f\x01d\u00FC+\f\u00A1\u00D3\u0090\\\u0098\u00FB\u00ED1\x04\x00\u00BE\u0099\u00ED\u0080\u00ED\u00DD\u00A7\u00F5\u00F6\u00D8s\u00FF\u00FD\u00DA\u00D9\u00CF}1\u00EE\x05\u00ED\x1E\u00CF\x10\u00C6q\x0B\u009F\u00EB\u00AF\u00FE\u00CD\u00CB\u00F0\u00BC\u00FF\u00B4o\u00B8\u00DA\r\u0083\u00A6\x02\u0098\u0087\u00A0O\u00BC\x06\u00F6\u00BB\u0091\u00851\u0083\u00E9\u009F\u00A1\u0081\u00AA*0\u00B7\u00A1M\u00BEG\u00A4\x1D\x10\u00903'K\u00BD\u00F2\u00EF*Rh~\u00AC\u00F8\u00DF\t3\u0085\u0084\u00D6L\x12Q\u00EA\u00C9\u00CC\t\x7Fpb\u009A\u00D9_ \x04)\u00D6\u00F2J\u00FC\x02\u00F4\x01\"m\u00B2X\u00AB\rAz=\u009F\x03j\u00A5/8Q\u00AF\u00F6QS\u00A9i\u00BB\u0088\u0087\u0099.\u00FC\u00F2\u0099nA\u00EE0U\u0099\u00C2\u0084q2\u0090\u00A8|\u0087\u00AAmu\u00DFG\x1F\x13\x19\u00883Z\u00BF\u00B7\u008E\u00DD\u00FB\x1B\u00E1~``tOE\x07\u00C8T\fu,\u00D2\u00F8\u00C8\u00CDr'\fAK\x05\u00EB\u00C33\x04\u009Bk7\u00BC\x1E\u008E!8?\u008B\u00B9\u00F0>{o\u00D0\u00F5\u00D3\u00D0\u00FF\u00D13\x7Fk\u00EDI\u00BF\u00F4\u00EB\u00B2~\u00F2!\u00D7\u00F9=\u00A5\u00CD\u008E\u00F5\x00n\u00AB\u008Dz\u00B3\\\u00D3_\u00FE{\u00EF\u00D7\u00E7=k\u00FF\u00EC\u00B3\u0098\u00F9\u00E2h\x10\u00F3\u00FF\u009B\u00AC\u00B6\u0080\u00A0X\u00DF\u0096\u00DA*H\x1B\u009D/I\u00C0\t\u009E\u00EF\u0090\u00E0]\u00CBy\u00BE\u00EC\x02\x11\u00D4H\u00C6z\u0092\x00n\u0089\u00AA\u00D2\u0096w\u00948Y\u00D8\u0091\u00A64\u00B7\u00FB\u00B9\u00D2\u00A3\u00FCwf\u00BA>\x1A,\x03\u00D2q\u008D#\x01\u00BB_G\u00E8\u00FD\u00D2 \u00E2\fC\u00A4\u00D0\u008C\u00E4\u00F8\u009Am`\"~\u008E\x11\u00A1\u00D7M$\u008A\u0080B\u00C5~\u00A3\u00C8\u00D7\u00CE\u00F9l\u00E1N\u00E5V\u00F3\u00A2\u00CD9$\u00E2\u00FA` \u00E2\bA(\u00F5;l{8I\u00CD\u00A4\u00DAb8\u00F1\u00CD~o]\x1D\u00BB)\u0080\u00D1\u00F5\x11\u008E\u0093\u00CA}\u00DAa\u00B3o5\u00C6\u00E5\u00FFF\u009C\b\u00D4\u009F\u00DB\u00DC\u009A\u00D6\x15\u00DF\u0089\u0097\u00ACG\u00B0Y\u00F0\u00D2\u00AA\u00C61\x01N@F$\x01\u00AA\x025\x06\u00B7[\u0091\u00AF]0t\u0085\u00EE\u00BF\x0E\u00FD\u00BD/\u00FEO[\u00BA\u00E7\u00FE\u00BA\u00BC\u00F6\u00DF\u00CB\u00EC4\u00C5=\u00B4\u00DDc\x19\u00C2R\x0F\u00B6\u00AB\u00C7\u0097\u00BF\u00F4\u00DD\u00FD\u00B7\u00CE\u00DD\u009A]\u008B9\x14\x0B8\u0087\u00F7\u0085\u00EB\u00B5}<\x1F\u00C1\x16\x12]\u00C9\u00A2\u00C0R\u0082\u00EC\u00D0}\u00A9\x19\x13w\x15\u00C2\u00A1\u00EF\u00E8o\u00B6\u00A9\x1A\u00B3\u00F1\u00F7\x1A\u00B6\u0084\u00A4ooE\u00E2N\x1A\u0097W\u00ADG\u00E00\u009CT\u00AA\u00F4\x10x\u00DEex\x06\u0086\u00F2\u00AF r2\u00E3\u00D9\x04*\u00CD\u00D8a!\u00A4P\x1Fj\x1C\x01\u00F2\u00BE\x12{:\u00B8\u008E\u00EC\u00A3\u00B1\u00E7\u00F2\u00B1\u00D2&\x00\u00EBK\u00A4\u00F4#:}\u00FCbK\u00B1\x1F]4J\u0092\x16\u00DF\u008F\bc\x1E\u008A\u00EA\u0084\x0Ei@\u00D4\u009F\u00AF\u00AEVa\u00C5&\u0085\x15a\u0081\x13\u00B3\u0082\t\u00EB\x12o\u00B8\u0099\u009A\u00E0oU\u00D0,/\x02\n\u00C5h\u00F3\x0F\x010\x022B\u00C5\u00FBq\u00F7\u00A5\u0084\u009A\u00A2\u00ABd<\u0099\u00BF\u00E0\u0093\u00A8\u00AF\u00BF0\u00B3rN=i\x12{\"\u008A6\u00EEG\x7F\u00EFy\u00CF:\u00B8\u00DC\u00DE\u00AD\u00E3M?-\u00C3)\u0087\u008AQ=\u00E6\u00ED\x1E\u00C9\x10\u0096\u00BAg\u00B8r|\u00F1\u009F\\\u00A8/zf\u009F}\x11\x03\u0080\u00A5\u00BB\r;\u0080\u00AE\u00CA\u00EA\u0087q\rQ\u00C1\b_:\u00B1 \u0092I(\u0080%\u00CC\u00A8(\x10\u008C\x10\u0080\x0B\u0085D\f\u00BBX\\\u0092\u00F4\u00E8\u008F\u0092\u0082\x12\b&\x05\u0093n@\u0098\u00C2u\u00A1\x11E\u00D8\u00A0b\u0084/\u00E1\u00E4\u009C\u00C5\u00EF\n\u00B7\x17\u0088\u00A3\x01f_F\u0092v\x04X\u00DBgJb\u00C0T\x06\u008E)\f\u0081\x1C\u00A3\x11\u0097LV\u00EA\n\u0083PJA\u0081\u008A\u00AB\b1\x7F6{\u00C67\u008D\u00C3\u00A9\u008B\u00D2`.\x05I\x00$\u00AF,\u00A7\u00C6\u0098\u008D\u00C8\u00B0\u0094\x01\u00E1NQMi^)\u008Eh\u0086\x1AO \x11\u00F8\u00DC\u008D\x10\u00C9\u00E0&\u00F1\u00B8b\u00CD\u00B2'\u00C68\u00B4\u008A\u0083\x0E\u00C5\x12\r\x1D\u0090%\u00B4\u0099\u00D12\u00EC!\x13\n\u00BF\u00BDv\x18Dp\u0098&\x10\u00CCT\u00A0\u00DB\u00FB\u0080\u00F7\u00BE\u00F2\u00DC\u00ADq>S\u00DDs\u00AE\u00C8\u00FD\u00EFq\x01L\u00F78\u0086\u00B0\u00A5\x07\u00DB\u0087\x17\u00BF\u00FF'\x7F+/x\u00E6r\u00D8\u008B\u00B9sc\n \u0093\x07d\f\u00F6\u00EA\x15U\x05\u00B0%\u00EC\u00CB\x12\u00C5\\er\u00C4\x17XG\u00B7eSP\u00BFi\x00\x1A\u00B0\u00DAH\u0092\u008C\u00C0\u009Ae\u00E7\u00F1\x07\u00814\u00B7*\u00A3\u00D4a\u00CC\u00C30\u00CC\u00E1\u008EO\u009D#\u00AB1\u00E4_\u00E4a\nU\x04\u0087\u00F8\u00F08\u0083\u0080\u00F9\u00EC\u00D9\u0088\u00D7\u00B2%+s\x00\"\u00BC*\u00F0o2\u0096\u00C9\u00D8\u009C\x19$\u00AD\u00B9\x11\u00AF0\u00BB\u00C9\u00D9b\u00E8\f\"@\x1B\u00C2//\u00BC\x17\u00FB\x10\u009B\u00F1@H\"6\u00F3\u00AC\u00D5\u0088\x02>2\u00DA\x0B\x14\u00C3\u00EA8>\u008C\u008E\u00BC>E/R\u00DD\u00E8\u00C6H\u00A1P,\u0091f\u00E1\x0ES\u00A9J\u00FF\x02Ge\u00BD\u00A0\x1CgD\u00BE7\u00DC4\u00A2tU-J\u00E45\u00F1.\u00DD\u00D1\u00E6hqm{\x0B\u00CB\u008B\u00FF\u00F4\u0099\u009B\u00E3\x12z\u00F0s?%\u00BB\x1Er\u008FB\n\u00F7(\u00860\u00EA\u00ADr\u00E9\u00F2\u008F_\u00FA\x0E\u00BC\u00E8\u0099\x07\u00DB\x1E\u00ACa\u0089\u00A6\u00E4\u00F7)\u00E7T\u009C!\u00888X\x04\x00\r\u00E2\x1E\u00E1h\u00D4\u00DFn\u0087\u00A9\u00A3K\u00AA\x06\u00E29\f\u00CA\u00FB\u00DA\u00BF\x12L\u0085\u00CC\u0084\u00EB\u00A3\u00FE\u00B7\u0090\x16\u00E1\u00A4\u00B8J\u0090+\x1D\u00E9\x02\u009CC1\u00F7\u0080\u00A2\u0099\x11=\x04i'pI.\u00E9Q \u00A9I\x1B\\b\x1398t\x0E\u00D1I\u00DD\u00DCg\u00A0\u00AA(\u00BE\u00B5;%xz7\u009A\x11\x00!@\x18Z\u00B4\x1COB\u0097B\x18R\u00E7Bh\x18,\u0093G\x03\u00A2T\x17\u00AC\u00C2\u00D4 \u0083\u00EB9\u0089!\u009E\x1D\x11\u00B8\u00F1\x12\x1AR{\u00BA\u00AD=\x19\u00A11l\u00A5\u00FBQ\u009A\x1Fi@\u00EF\x10LK\u00DA\x01\x1D\u00AC\u00A2\u00CD\u00BC\t3\u00DCv@\x17\u00BEz\x16\u00C6\u00C0V=\x05\u00AB\u00A8a\u00F2Uv\x1E?ls&\u00D7\x01H\u00C3l\u00DC\u00C2\u00F8\u00F7\x7F\u00F6\u00CC\u00ADa\u00B6\u00D4\u00C5-\u00FFJ\u00E6\x0F8\x1A\x16s\u0097\u00B4{\fC\u00D0\u00AD\x11\u0097-_\u00FE{\x7F\u0083\u00DF>\u00F7\u00C0\u00FCzxZ\x0E\x06 \u00F6U\u00E6\u00FA]\u0080\u0081\u0083\u00B1Ja\u0084\u009E\u00F2\u0090\u0088\x00\u00FEy\u0094\u00A9\u00A4\x17X\x18;\rl\u009DF# P\u0085\u008B\u00F8\u00EC7`B\u00ED\u00C9\u00FB\x13\u00B8ZA?\u0085\u00FB5t\x0E\u00E1w)\x01E\u0081\x10\u00F8\u0094D\t\x04\u00E3$b\x0F:\u00A2>\x1E^\x05J\u00B3\u00A2R\x04s\u00A0\u00B4GJ\u00D9\u0096q\x06\u00C6\u009C\\\x01j\u00AE\x0E\x18lr\x06\u00E0\u00E2]\u0087\u0088\u00E1\x07\u00ABR\u00C3\u008E9y\"8bw\x1C\u00A6\u0085\u00B7\u00C0&-=\f-$\u00B3\u00EA\x00\u00B4fx\u00AE'\u00A22\x17\u00A5]-\u0092\x18M\u00BDc\u0081@\u00DBhs\u00C2\u0098\x0E\u00D1\u009C\u0093@\\\u009E8%\x03\u00C4sO\x04n\x10E\u0087\u0088[\u0095\u00B4\x032\x03t\x01\u0095\x11\"N\u00B9\u00BD2#\x7F\u009A\u0098\u00CF\u00FC\u00E9h\u00DB\u00FAr\x13\u00DB\u00EFy\u00D9\u00B9\u00FBe}\u00BF.n\u00FDw2\u00FF\u008A\u00A3\u00EF\u00ECNl\u00F7\x18\u0086\u00F0\u00A1\u00F6\u0087\u00BFy~\x7F\u00FE\u00B3n\u009E\u00DF\u0080\r\u0095p\u00ED.\u008B\u0084\x18\x1C\u00D2Sr\u008Dj\u00CB\u00C5\b\u00DE\u00DE\u00D3\x120i@\u00FA0`\x1A\u008B\u008C\u009F\u0093i\u00E4yD\x15\u00FCNx\u00ABJ\u00C9d\u00AD;\u00BD\u00ACj\u0090*\u00E2\x10v\u00CD\u0099@a\x04\u00D5X\x18\u0084\u00DB\u00CC\u00CE\u00E0\u00C7\r\r{\u00CC\u00814\u0087\u00BD\u00C2\u00CE!\u00AD\x01}@\u00D8\x01\\\u0095\u0080\x1B\x013i\u00A9\u00C4\"\u00F8yS\u0089V\u00D4\u0089\bT2\u00C9n\u00F7*\u00B1\x16\x01\u00CB\u00DA\u00B4o\u00F4\u0082\u0084\u009C\u0088\u0088.B\u00C2W\x1D\u008AP_\u00CAW\u0093\u00DC\x12F\u00C9\u00A2\x0Ex\x1F\u0089*\u009C\u00E1\u00C0\x18\u00958S\u0096\u00F0&\u00901\u00E43ReP\u00FF\u00ACq/z|L\u00BD\x10\x1A\x1A\u00A5\u0085\u00FA\u00E0b\x04\u0081\x15w\u00A8\x0FD\u008De\x0F\u008F\u00BA\x16@\u00DBMY%\x02\u00EF\u00BBA\x1B0loB\u00DF\u00F5\u00B2g\x1D\x18\u0096{\x00\u00FC\u00FA\u008EN\u008EA[]\u00D3\u00C7\u00A4]\u00BE|\u00FD\u00CF\u00BF\u00B6\u00FF\u00DA\x7F\u00BBi\u00F6q\u00CCDqB\x17\u00CC\x04\u0098\u0089bM\x1531S\u00DC\x1AJ$\u00A2*\u00E6\u00B0c\x19ld\tM$\u00C5\u00994Ot\u00B2\u0090f\u00F1\u00F3\"&\x01-\n\u00A8X\u00BD\u0084\x15\u00F9\u00AE\x04\u00FDN\u00BE\u0092\u00E5\u00BB\x01\u0094\u00C0\x19z\r\x06\u0088\x0E@\u00F7\b\b\u009D\u0095\u0085G\"u\u00CF\u00C2J\"\u00920\u00F8(\b/%^\u00A2\x01z(d\u00B2\u00F0\u00E3\u009C\x12\u00C5[\u00C3\u00A5\u00F3\u00BCr\x0E\u0088\x16\u00A8~\u00A0\x04\u00D7\u00F8aU\b}s\u00B5`IHc\u008D\u0090]\u0085Z^A\u00A0\x01\u009D0\u0085\u00DCI\u00DA\u00FF\u008D\x18\x05\u00F6\u00AB\u00C8\u009D\u00B2=\u00A7\u00C13\x1FA/\u0088\u00C7\r\x00\u00DEOw\u0086\u00C1\u00A6c\u00C44\u00D89\x16\u00AB\u00A0,\x19\u00DF\x01\x19\x1D7v5\u00B5\u00A3\u00BBQR\u0097\x1E\u00CB\u00D03\u00A6A\x17\u0088 \u00A7\u00D1\u00E7\u00A3\u00FA\u00B2\u00BB\u00F8\x1E\x1E\x1C\x0B\u0091\u0092]\x16\u0099\u00A8\u00BD\u008C7\n\u00B2\u00F8s/\x05\u00DB\u00EB\u00A7B\u00BF\u00FB'\u009F\u00B5\u00F1\u0094\u00FFr\u00CC\u0083\u0097\u008E9C\u00B8r\u00FC\u00FF\u00CEy\u00F5\u00F8\u00BC\u00D7|j\u00FE\u0081\u00B6\u00A6\u00C0\\\x04\x1B\u00AAX\u0093\u0086\x19:\u00D6\u009C\u0080\u00E7h\u00CE\x10j$\u00A2\x05)\x19(\u00AF\u0084\u00CE\u00C2(\u00CD\u00CDy\x1A\u00D9\u008C5\u00A4\u0099\u00A9\u00CF3\u0098\x15XD'\u00C7\u00C3\x1F\u00A0\u0099\u00E3\x00\u00A0\u00ACaIu@\u00E7\x16e\u0088\u0099\u00A5\x1DC\u0090a\u00C65\u00A2\u0090\u00C1D\u00E6YP\u00FF]\"\b\u00C8n\x10j\u0080\u00BA\u00D7\u00A1J\u00E8\x12D\x14)\u00D7\u0090\b\u00CCc\x1F\u00D3\u00AD\u00DC\u00E0\u00C4\u00E9\u00B1\x05N\u0090\u0096\x1198\u00F1J\b\u00FE\u00E4|I\u008C\n\u0084\u00E1\u008D\x12>\u00F7\u00A4@\u00BA\x10W\u0082\u0092\u00F2\u00B7<F=?j\x1E\u0094\x00%\u00F0\u00FE\u00BE\u00A9\u008A\u00E16\x1B\u008B\u00D594\u00E6\u0091\u00F5\x12$\u00CE7\x061\x16\u00A6\u00B0\u00B4>z\u00B7\u00FEGEl\u00E7\x16\x01T~~Wg\"dN#\u00B4/\x01\u00ED\u0090\t\x03I&\u00F9%3\u0084Q\u00A1}\u00C0b\u00F7\u0083\u00FB\u00F2\u00EC\u009Fy\u00DA\u00EE\x1F\u00F8\u008F\u00C74\u00CC\u00F9\u0098\u00AA\f\u009F\u00D3\u008B\u00BF\u00EB\u008F\u00B7\u00FE\u00C6\u0092\u00E1\u00EF\x00\x00 \x00IDAT\u00C3yW\u00CD.kM\u00CD)\u00D4`j\u0082 \u00EB\u00FA\x01\u00AE\x1A\u00B8\u00BBQ\\\u00CF^\"d+\u00A8y\u008B\u009A\u0083\u00C9T\b3E\u00A6\fQ\u00F7J\u00A8\u00DB\"XhUC\u0093W\x15\u00F74\u00D0\u00ADiq\t*\u0099/A\u00B7\u00A6b\x06\u00E8:\u0098U\u00D1\x18G\x10*\x02\u00D5\x04\u00EF]\x05\u00E6I\x18,~`\"\u00C1\u00DBDUI\u0086R\u00EC\x0B<7\x14\u00EE\u00C2h\u00AAB\x1Bq\x04U\u00E9\u0095\u00BC\u00954\u0097\u00E6\u00E5\u00B8\u00B8\u009F\u00BF\u00B1\u00D0\b\u00FB\x0F\u00B8\x10s\u009C0\u00A2\u008E\u009B\u0090}\u00F58\u00A6\u00D7(h\x11\u0088w<a\x1E\u00E5_\u0083\u00FA\u0080\u00A2\u0083\x1B\u00B5\u0084j\u00D1X\u00DE\u008E(AA\u00E3j\u00DE\u00DE\u0099\x1C:,\u0094\u008Dc\u00F2\x10i~\u00A7\u00D7abL\u0094\u00F2G\u00F5\u0081\u00ABB\u00CB\u00AAJ\u00A5\u00F3\u0088\u009B\u00C2\u00E7]1\u00DB\u00FB\u00F9\u00B6\u00BC\u00E8e\u00E7m\u00BD\u00FB\u00C5_X\x7F\u00FC\u00CF\x1F\u00B3\u0084\u00A8c\u00C6\x10n\u00D2+\u00BF\u00E1\u00A5[\u00BFr\u00FE\u00C5k\x7F\u00BF\u00B1K\x15k*\x18\u009A@\u00DD\u00BC\u00D7U1\u008A\u00ED\u00BBg\u009E\x063&\u009A\u0085\u00D9\u00D6|Ss\x1F\u009A\u00C6\u00D7\u00CC\u00A5(\u00D5\u00CB`\u00CB`\x12\u009D\x17\u00CBB\u00E2\u00DB\b\u00AF\u0087\u00E0:0e\x12\u00ED\f\u00C2\u00FB\u0082\u00C6\u00B4\u00E6\u00ACa\u00DD0\x0BC\u008D#\f\u00D9\u0089\t\u00E5\u00B3x\u009C\u0081\u00CE|\u00BD6\u00D8\u00FE\x02$j\x06!\u0099\u0094\x11\u00A1m\u00C1\u008E\u00F1\u00CEP\u0084\u00B5\x1A\u0092L\x04>'\u00A1\x02x_\u00E1*\u00F49H\u00BF}\x03Z\u009B\u0084s\u00B2/\u00EA\u00E7\u009C\u008B\u00EC\u0093\u00A4?\u0086\u00E7f\u00E2\u0082\u00F3/v\u00F5\n: \x1Bh0\u00C9,\u00FE\u00CE\u00F8@\u00A4A\u00AA(\u00D4aD\u0081n\x1E\x16\u0086 \u00AB\u00F4\x18\u008BMFs\u0091L&\u00EA\x04\u00CE\u00F7\x1EF\u00D8\u009E\u00B6\x07\x7F\u008E\x12B\u0089p\u00B9b\u00CC\u00B9\u00E7\"@2D-\u00CC@\u00F8\u00DC+<ygc\u00DF-\x19`\u00D7x\u0087\x02\u00C5\u00DA\u008D\u009F\u00DC\u00D8z\u00FB\x1F\u009E?^\u00F6\u00CA\u00C7\x0E\u008Fx\u00FA1I\u009D>&\fa\u00AF~\u00EE\u00A4\u0097m\u00FD\u00E6\x05\x17\fo:eC:\u00EA\u00B2o\u00BE2m\u008E-\u0085\u00A5I\u00C6\u00A9\u0091\u0088\x07h\x18\u00F7\u00962\u00F5\u00D2\x03\u00C9\u00D3k\x135oC:0=b1z\x15\u008C*\u00E8b\tSc\u00C82.\u00AD\u00E6\u0098\u00C3c\nt#z\x00\u0086\b>\u0082\x1B\x17Y\u00C3\u0090\u00A8\u00A0Ju\x01\u00DD\u008A\x1A\x18\x07\u00EA\x01L\u00E4l\u009A\u00AB,\u00D0\u0083\f\u00D0F4T$\x13\u00E3\x12\u00FCX\u00EC\u00CD\u00C8\u00D8\u0086\u009E\u00BD$zh\x11\u0092L\b\x1BFJ\u00E1y\u00D5MN\u00F68\u00C0\u0082\u0094\u00C4\r\u0091\u00CE\u00CC\u009C\u00F0\x05\x1A\u009Fsl\u00894\u00D2;+\x1E\x04\u00A2P\u00A9o\u00CB\u0098\u0096\x05]u\u00A7}I\u0081\u009D\u00AC\x0B\u008AE\x12\u009A \x11\u0084\u0088{\n\u008A\u00DB\u00D6v\u0089\x01\u00BDS;\u0098\u0099\u00ABTTO\u00AA\u0091R\u00FD\x02\x11q\u0086\u00BA\u00F4a\u00F4\u00C9{:\u00E2V\u009C\x193U\f\u009F\u00FB\u00C8)\x07\u00DE\u00FC\x07\x17\u00E8\u00E7.|\u009C<\u00E4\u00BB\u00EF\u00F6\"+w;CX\u00E8bx\u00CD\u00F6\u00EF\u00BC\u00E6\u00E5\u00F8\u00B33g2\u00A2\u00F9\"\u00A4\u0083\u008D\u0084_u\u00FE\u00D1\x7F#`\u00B3(C\x7F\u00FF\x0E\u00B9\u00C4\t\u00BC9q\u0093\u00EB2\x1C\u00BEC\u00B0\u0094\u00EE\u00A1\u00B9\u00F6\x06\u00E8\u00A10\x15\u00C3\u00A4\u00E9\f\x04\u0086\u00F4\u00E33/\u00C2=\x01X\x03\x03\u008DDf\u00E6\u00C2\x13\u00D3\u00F15P\u0082\u00BB\u00BE\x1A\u00F5\u00FD\u00C1\b\u0093\u00CC\u0080D\u00A4\u00A6\u00B0$K\u00F4\u0085.-\u00FC\u00F2\u00D3\u00A0\"\u0097xe\u009FH\x1B\"\u00CFQ\u0097l\u00B47\x18\u00D22\x15'\u0099L\u008F\u00D9\u00B3\x19h\u00AE\u00D3\u00D6\u00A8g\u00A6?\x13_\x01pz\u00A6;\u00D0\u008C\r]Z\u00BC3;\u00E6~\u00FF \u0094\u00AA\u00BD\u00D8{\u00EA\u00EE\u009ElE'Tm%\u008F\u00A0\u00A8\x0F\u00E2\u00DF\u0093\u0087#\u00FCF\u00AA\u0080\f\u00E6.D\x0BI\x1E\x1E\u0087\u0098\x13G!\u009D\u00F3I\x1F@\u0089\u00C9\b\u00A2v/\x00Fgl\u00CD\u00C3\u009CGD\x15Ww\u00C9&|PL\u00F6\u00EB\u00BC\u00CDv(Uj\u00FA\u00DC\u00F3\u008F\u00BF\u00E7\u00CC\u00AD7\u00FD\u00FEk\u00F4\u00C0g\u00FF\u0099\u009Cp\u00F7\u0096c\u00BB\u00DB\x19\u00C2\u009B\u0097\x7F\u00F6\u00C2g\u00E3w\u00BF\u00EF\u00E0\u00DAA\u009C\u00EC\u00849\u00F8+\x1E\u00A1Xz\u00ED\u0082\x11\u0082\u00B1\u00DBk\u00A0\u00CD`\u008Cs\u00D5\u00ED\x07\u00BEdI\x1F\u00C8 $\u00F5\x05\u00DE\u00C5Q\u0087;\x14M\u00E6\u00FB\u00A2piU\u00DD\u008F\u00DD\r\u008B\nE\x17[4\x1A\u00BE\u00865\x003\u008C:G\u0093\x19\u0086\u0082\x06B\x12\u00AB\u00D9\x10\u00CC\u00D8\u00C7p\u00E4\u0082\x10\u0084z\u00AE\u00A9\x11\u00A2\u00EAIJ-\x17\x1A\u00C3\u00FC\u00DA\u00AA\u00AB\x0F\u00A6fpM5\u00F3L\u00A8C\x7F\u0091b\u00C0\u00F4\u00C8\u00BB\x18#<\u00BB\x0F\u0080\u0088Ey\u00A4\u00844\u0082\u00A0\u00A1\x0F\u00C1^}\u00A8\u00E2\u00D2X\x11\u00CCT(\u0091\u009B'\x16\u00C5~\x07\u00AErHs;\u00A8\"\n\u00B0\b|\u00F6\x07W;\u008C\x15B\x05\u008D\u00B9\x0BN\x14\x16p\u00A4\u00B0\u00C2(+\u00E2\u00DC\x19S\x07\u00D0d\x16H\u00C2\u00E2@<\u00AA\u0091h\u0081U\u0099\x18\u008F\u00A0\u008C\u00A7\u00F0't&b\u00D6%\u008F[@)\u00A4\"#\u0088R\u00C2;\u0083f\u00B9\x11tO\u00EA\x12io\u00F1\x19%wsf\u009B\u008C$\u00BF\u00FA\x0BE\u0085+\u00D2\x05k]\u00B1\u00BC\u00E4\u0082\u00EF\u00DB\u00DE\u00F5U/\x04\u00F0\u008B\u00B8\x1B\u00DB\u00DD\u00CA\x10.\x19\u00FF\u00E6\u00C7~v\u00F9\x0B\u00BFp\u00DD\u00B0\x17\u00A2\x1Dk\x10\u00CC\x05\u0098k\u00C7\u00DC%Q\u00A4&\u008B\x04B\u00E0,\u008A\u0087\r\u00AB\x18\u00D1\x0F\n,]\x124\x15\u008C\u00CE8Dh1\u0090@\u008F\u0084\u00B1\x1D\rcx\f\b\u0093\u00BD\x1F!\u00BA \u0082\x00:\x064\u009D\u00A3\u00CB:\u00809\u00BA\x17r763\u00A0\u00D68\u00AC5\u0098\x02\u00C2\u00F3)\u00C2\u0088\u0098\nR\x1A\u00C1\u00F8\u0099\u00AB\u00858\u00C5\u00C7\b\u00AA\x01\u0094\u00CE\x1C;|!\u00931%2\b\u0098Mb\x17\u00FFQ\u00A4\u00AC[\x0F\u00EEqF\x10\u0091\u00CE\u008AHpj\u00E1\x1D\x002\x00\bH\u00DF\u00A63\x10i\u0099\u00D9Y\u00D4\u009C0\x04R{\bJ\u00A8\x19\x16\u00DD\x19MB\u00F4tw\u00F8|uC%6\fC Lz\"\u0083\x17\u00D0>\u00B2,\x04\u0097R<\u00A2\u00B9+\u00BB\u008B\u00B1y\x16L\u00B5)\x04\"\u00E0@MY\r\u00F1\u00A4\u00DD\u00D1\u0089\u00A0\u0084\u00CB\u00A1\u00DC\u00FC0\u00AD\u00B2\u00DBC\x1F\u009E\u008D[\u00D8|\u00DF+~a\u00F3M/x\u00FF\u00C6?\u00FF\u0095\u00BB\u00AD\u009A\u00F3\u00DD\u00C6\x10\u00AE\u00EE\x1F|\u00F8\u00AFn\u00FF\u00EA\x1F]\u00BA\u00F6\t\u00A8,\x00\x15\u00EC\u0083b\x03\u0082\r\u00C0+\x1AYu\u00E49\u008C\u00B8G\u00E9\x18\u00FD}\u008C\u00A2\x1E\u0098\u0084\x00oKAl\u00D1f 8\u0093\u009E\x04\x12\u00A1\u00C9U\u00F5\u00A4i\u00D1\x12\u009B8:;q\u00F4\u00C5lK\u00B0ad\u00D8\u00B1\u00B1.d9\x16E:>[2\x05\x1A\x0F\x03\u00C2\u00B3\u00B2Bs\u0098O\u00C8\u00EF\x06A\u00F5\u00DF\u009D\u0098\u00A3\u00CAR\u00E8\u00C3\u00FC\u00EE\f\u00C1Q\u008Ee<\x0E\u00C8\u00D0b\u00B1\u00AC\u00C6@\x0F\u008A\u0089\u0087\u00C3\u00C5\u009BiW\u0092nG\u00CE\u008E\u00DF+\u00A6\u00C3\u00A0\x12\nwp~\u00A5N[\u00DE\x7F\u00C4&9Y7\u00A60k\u008C\u0083\u00C6X{\x00'\x04\u00CD\u00F7\u00D1\u00DD(\u00C8B\u00AB\u0089@\u00FC9\u00AA\u009D$xM\x03\u00D5\u008F\u00A9\u008D\u00C3\u008Fu\u008F\u00C4t&ch\u008D\u00CA:W\u0089\u00C6<\u00C7|\u00A2;\"(\x0B\u00C6\u00DF\u0087B\u0090\u00E9\u00E9\u00DD\u0099l\u00F7\u009A\x13#B\u00D2+\x19Im\u00B7\u00C7 \x0E\u00D1\u00A4c\u00E3\u00C0\x17q\u00F0\u009D/\u00FF\u00A3\u00E5%\u00E7]>{\u00F4\u008F\u00DF-\u00FB>\u00DC-\f\u00E1:\u00BD\u00F6\u00C4\u00FFg\u00EB\u00FF~\u00DD\u00EBgo\u00DB=zv\u00A1\n\u00B0\x1F\u008A\r\x00\u00BB\u009D\u00D8\u00E7\u00A2\u0098\u00AB\u00B9\x13\u00E1\u0084\u00EBk\x18J\u00F0\u00AEf7\x1B\u0085\x0E/\u00F7\x06\u0088\u0086l\u00CF?_L\u00AA\u00EE\u00FEe-\u0085\u0089\u00DD\u00DE\u0092\u00A4\u00C4\u00E4\u00CF\x00\u00AF\u00C7\u00A4\x16\u00E1\x00Y\u0087\u00CA\x1C\u00AAV@\u00B3\u0083\u009E\u0081\u00E6\f\x01\x0E3kHr\u00F54\u0098\x112\f\u0080\"\u00A9\u00F3\u00ABDxn\u00D4i\u00A2\u00FB/\u0098\x03\u00E2\\\u00B7\u00C9\u0083\u0099\u0089\u00ADY_\u00EA1\f\x00\x11j\u0089\u0092s\u00C8*\u00AB\u00C8\u0080\u00A9\u00C6\x0EL\f\u0095;\u00EA\u0098l\u00B0\"\u00FE\f\u00E2\b\u00A20R\u008F\x11`\u00BD\u00C2\u00A6-\u008D\u0099\x1C\r\u00D1\x19\u00BF\u00A7Ng$\u00E9\u0086K\u00A6`k\u00F3\u00D4\u00E4\x0E3\nJ\x0B[\u008E\u00BD,\u00CE\u0087\x02\u00DA\u00ED\u00B9\u009A\u00D9\x07\u0094\f\u0085\u00CCK\u00C9\u00E0\u008DX5\u0090\u00D2\x10\u00C7L\u00CDr\x13\u00B4$Z\u00E2\u00D8\u00B5\u00D3q=:j\"\u009B$\u00D4\u00A7w\u00C8\u00D5\x0BzL\u0094\u00C7{\u00BC\u0083T\x1FJ+?O7\u00E31\u00E6\u00B5~\u00DD\u0095\u00BB\u00B7\u00DF\u00FA\u00B2\u00D7\u00E9\u008DW>Z\x1Ex\u00D6]\u00BEC\u00D4\u00DD\u00C2\x10\u00DE\u00BC|\u00FD\x1F\u00FC\x0Fy\u00C5Y\u00E3@\u008E\r\x00f\x07\u00D8\x03`\x17\f\u00A22\u00B8\u00A8\t\x17\u008B\u00BB\x1A\u00D5\x10\x04\u00E3:h?`_\x16\u00A3 XJz\u0088=\u0080\x0Es\x00py>\u00C0\u00FA0f\u00C2Q$\u00CA\u00B0W?@\u00B0\u008E\u00E6\u00EA\x01\u00D3\u0095\u00BB'\x1FY1\u0096\x01c\u00893hn`J\u0095\u0080jDa\f\u008CjR\u00C2{\tn\u00A7\u0091P4\u00F89dU\u00C3!\x16Q\u00DA\x13\u00D4\u00A5;},\x12\u00C7\u0081\u00F0\u00CD\x16\u00C6B\u00A1\x17\u008B\u0090\u00D2\u0096C\u00F3'Hwg\u0089\u00E2H|\u009F\\:\x16;\u00BB\u0090\u00A2\u00D2\u00F8\u00AF$\u008C\u00F8\u00C9G*H\u00C4`\u00D4\u00E0\u00844P\u00A6\u00DB\x7F\u00C5\u00C2\u00C6\u008D\x10\u009D\x00\u00B9\u00D7]\x10ZU\u00B7\u00AA4\u00EE9\u00E7\u00EA1)\u00EAy\f\u0081\u008A\u00A8\u00D2\x144F5\u00C9\u0089\\\u00C3`H\u00C6\u00C2\u00ACI+\u00D3#D\x04\u00E1\u0095I\u00C7\u00F74\u00AA\u00B4\u00BEK\u009D\u00FE\u00A4\u0089\u00A7j\u00D3\u00A6\u0098]y\u00D1Y\x07\u00CE\x7F\u00FEK\x00<\x03wqk\u00B7\x7F\u00CA\u0097\u00D6\u00FEn|\u00DBO<\u00BF\u00FF\u00DE\u00BF\u00BCem\u00D3\x7F\u0099.\u0098-\x007w\u00C1\x01\x05\u00B6\u00E1\x7F*X@\u00B0\x00\u00B0\x10S\r\u009605b\u00E1\u009F\u0097Hc \u00D5\u0087\u00B12\x03\x18\u00BB\u00E8\u00E5\u00BC\u00E2\u00E1\t\u0090\u0097\x7F\u0086\f:\u00D61b\rK\u0099\u00FBw\u00EE\u00F6\u00D0\u00CC\u00D0)3\u008C\x180\u00A2a\u0084\u0095e\u00EDbj\u0080\u00B6\x01*\x03\u00D0\u00D2\u008E\u00E0\u009A-hs\u00C8\u0090ff?\u00CE\u00D2\u00A8\b\x01\u00CB\u00A9A\x06\u00A0\u0089\u00B9\x18K\x1A\u00B3\u00D9\x0BL5\u00D1\u00E6\u00FF\u008A\u00C7gj\u00AA\t*\u0083\x1Fs\x06\u00E4}(\x1A\u00BA\u00D7`0\x17g\u00B3\u00F1\u008A\u00A9/v\x1DK\u00B8!\u00CB\x12\u00C2\u00A4\u00AB\u00D9,\u00FD\u00D9\u00A4\u00F9\u00BD%\u00FAS\u0099#b=e\x06\u00C8\u00DC\u00FF\u00EC\u00B3\u00CA\x1C\u00C6\u00A6\x1D\u00811\x03\u00D4\u009F\t\u008E\u00C0\x04\x16\u00D1\u0099\u00CFkc\x17&j\u0089\u00C4\u00BCU4e\u00CF\u00E0\u00CF\x1B\u00CF\u00E4\u009F\u0083i'\u00A3\u00CF\u0087J[\u00D0\x0E;\u008Fr<\u00F6\u00A7\u008E\x14w2\x7F\u00FE1\u00CD}\u0096(5\u00DA\u0091\u00A9\x0FV\x1D\u00BC\u00A3\u00BD\u00EF\u008DO?\u00F8\u00C6\u00DF\u00FE\u0089#\u00BA\u00F8(\u00DA]\u008A\x10>\u00AB\u009F>\u00E3Y\u00DB?\u00F3\u00E2\u008F\u00AD]\u008D\u009D\u00BA^B\u00DC}M\u00B1\x0B\u0082u\b\u00E6\u00A2\u00E6\u008F\u0085\x05\x05/\u00E0S\u00AE,u!\x1Epd\x10\u00BF\x03\u00F1]\u00DCX\u00D8\u00E2\x18\x19D\u00AA\x07K\u00CFGH\u00B3\x1D\u00E3\x0B\u00E6\u00E8:\u00C3(\u00EB`\u00D4!\x03\u009E\x15\x03\u00BA641\x06\u00D0\u00D4\u0088\u00CA\u00CD\u00A0h\u0093\u0085\u00D4\u00A0\x13\u008F\u0082\x13\u00B2\x0ENX-~\u0097N\u00E3\u00A3K?\u00D8B\u008E\u0094gM\u0087l\u00CC\x19\u00DDs!\u00D6K>\x02]h\u0094\u009A\u008A\u00D0\u00C9\u00AD\u00C0\t2\u00AC9R\u00A8{~\u00DF\x11\u00D7\u0080@6R~WwwB\u00A8Z\fn\u008F-\u00EA\x04u\u0082bhS\u00A2\x02A\tSN4\x02\u00B1\u00DC\t\u00D6Q\u0088x\u00D40Lj\x10\u00B0\u0094d&cVn\u0098,v\u008A\u0090\u00F6\x00Pl\b6_\u008CTl\u00B9\x1CUa\u00F5.V\u0095N\x12\u00FD\u0098\u00EF\u00C4\u008B\u00C1\u008A\u00BB)s\u00DE\u00DD\u00D8\b\u0081H\t\u00A1\x0E\u0083K\u00A6=\x11D\u0088j<\u00E2\u00A4)\x02\u00B5l\x1C\u00B8\x05\u00FB\u00DE\u00F9\u008A\x17/\u00AEx\u00CD\u00BB\u00E7\x0F\x7F\u00DA]\u00B6\x15\u00FD]\u00C6\x10\x0E\u00EA\u00DE\u00D9\u008B\u00B6^\u00F8\u008A\x0B\u00E6o9\u00D1\u00EA\u00F0\u00D5$f\u00FE\u00D9bQ\x00\u00B7\u00C0\u0098\u00C2\u00CC'\u00A5\u00A9\u00D9\x15\u00C8\u0087\x07\u00A4\u00FA\u00D0\u00C4\u00C3\u0093\x15\u0090\u00F0F \u00EA#0]vt\u00AB|\u00B5g7G\u00D2\u00B6<\x1A\u0080\x19T-K\u00A23\u00BE\x00n/\x10+\u00BD\u00DAK\u00A6b\u00C3\f#\u00A5\u0086\u00B8g\u0081\u00AE\u00BE *'\u00FA0k\u00D3\u00ED\bp\u00F1h,\u00FA\"\u0091\u008A\u00AB*\u00AD\u00D9T18}\x1E\u009D\x18\x1E\u0085JT.\u00AD\u00C3bo\u00F3\u00AD\u00939\u00AFYx\u0084\u00EE|/pI\fD\u0090R\x10\u00B9C\u00F5\u00D0M\u00CC\x06aR\u009BC\u0099\u00E8\x0F9\u0086J\u0097\u00F5p\u00B42\x16\x0E\u00A8\u00FA\u0092\u00C3\x1E\u00C0\u009C\x06'\x1E\u00B7#\u00D8}\x06\u00EFi\u0084\u00EA2\u0099G0\u00A4\u00F46\x04*\x10\u0080\u00D9\u0087\u00D9\u00F7\u0088(\u0082\x13\u009E\u008E2\u00C7\u00D2<\u00F1\u00C9\\\u0094\x18\x07c\x022&\x12\u00D3% \u00DD\u008D\u0090\x03R\x1D\u00CA\u00F5\x1E\u008F\u00E9?L\u0083\u00A4\u0080Pa\x14f\x10m\u0082]\u009F\u00FF\u00F8\u0089\x07\u00DF\u00F4\u00A7\u00AF\u00D0[>\u00F3]\u00F2\u0080\u00AF^\u00EE\u0098\u00C6;\u00A1\r\u00B7\x7F\u00CA\u00D1\u00B5o\x7F\u00CE\u00A3\u009E\u00F3\u009Bx\u00DE\u00D3o\u009D\u00DD\n\u0095\u008E|\u00DE\u00E9\u00E2\u0084\u00FF\u00D7\u00DC\u00C1\u008254/q\u00A6\u00BE=\u009B3\x04\x117vk\u00AC\x1D\u00BA\x0E\x05\x00\u008B\u0087\u0088\u00A4\u00DB0\u00D5\u00D9B\x02\u00C2o\u00C6\f\u00809D\u00D6\x00Y\u0083A\u00E5L\u009F\u00CA\x14\u00E6\u0099\u00FB\u00EE\u00FDw\u0087\u00AB\u0082\x01\u0083\u00E7.\b\x04M\x04\x01u\u00A5\u00C0^G\f\u00BC&\u00E11\u00AD\u00D6\u008E\x06\u00DC\u00C0H\u0098ly\u009A\u00CE\u00D0\b\u00AB\u00A3\u009E\u0082\u00CFeQ'\u00D8\u00AF\u0090\u00D1DNE+4\u00CF-i\x1C\u00867\u00EF[[<\u0087\x05<\u0091\u00B90\u00D8\u00AB\u00F2\r\u00BBg\u00D4\\\u00F0\u00E3\u00B9\u00E35]\u0081-\u0098\u009E\x10\u00AE\x0B \u00EEuI\u00ECA\u00A3g\u00FE\u00C6O\x11v,\x16\u00C5\u00D9d%~\x02\u00EE\u00E5q\u00E9+\u00F0\u00C5\u00C4\u00D4q\u00A6\u00AD\x07s\u00A1\u00DA@\u00E4\u0094\u00C4\x18k\u0083a\u00D2<Ki\u00BE\u00AE\u00AB\u00B8\u00A8*\u00BC\x01\u00EFA\x01\u00E2L+7\u00BD\u00C9>'\u008C \u008EI\u009EP\u008D\u00CA\n\u00A0+Z\u00EFh7\\\u00F3\u00D0\u0083\x1D\u00E3\x7F\u00FE\u00CB\u00F7\u00BC\x13wA\u00BBKl\b\u009F\u00D2+\x1E\u00F9?\u0096\u00BF\u00FF\u009C\x1B\u00E6_\u00F4_\x0E\u0089\u0085&\u00BFt\x00\u00B7\u008Ab/\u0080\u0083*\u00D8\x12`\u00E16\u0083\x11\u0096\u00A46\"\u00ED\x07\u00F69\u0099I\u00A8\x10\n\x0B?\u008Ec\u00F4<\u00A4}\u00816\u0083\x11st\u00DB\u00EA\u00C5m\b\f\u009E\u009E\u00C1\u0092\u00AB\u00D7L\u0095\u0088|\u00C9T!\u00EA\u00F7\x0E\u00C1\u00E8\u00EA\u00C4\u00A4\u00E8{\u00842\u00A7\u009A\u0090\u00B5\x15\u00CB\u00B1\u00D5]\u0099\u0088(\u0088@\"\u00CA1\u00ED\x01\u00A9\u00BFS\x0F\u0097<G\x19Z\u00CD])\u008C)\u0089\u009A\u009D!k46H\u009FC:\x170%\u00DA\u00E0\u00C4T\u00C6\tVy\u00AE\u00EE\u00CC\u0086d\u009E\u00ABI]\u00E5Y\u00CA\x1F\u00B7\u00A2c\u00D2z-&cP|6\u00BD\x07\u00EF\x13\u00C4\u00BA\u00F3\x1E\u00E9\u008F\"cL\u009B\f\u00CD\u00C9\u00B9\x7FW\u00CB>\u0094\u00D7S%\u0090$F\u00BE\x0F/{\u00C7\u00C4x\u0089=1\x1C\u0091M\u00E2MZ\u008C]&\u00E3\u00A4\u00F0\u00E1\u00B3\u00DFN\u00A3As\u0095n\x04\u0098\u008DK\u00B4\u00F7\u00BE\u00F19[\u00EF\u00FE\u00E3G\u00DE^7G\u00D3\u00EEt\u0095a\u00EF\u00C1/\u00CC\u009F\u00BF\u00FD\u00C2\u0097\u00BDc\u00ED]\u00F31\u0092]\x0E5\x05\u00AB\x18\u00C9\u0088z\u00AF\x00\u00BBT\u00B1\x0E\u00C16\u00CA\u0094\u008A\u00A1\x00\u00BE\u00E6\u00EEn\u00AA\u00A5d\u00A6\u00E2\b\u00DA\x10\f&\u00AF\u0094\u00D8\x07m\u00B8\x1D\x03D\u00E7hn8\x1C}\u0081Z\u00C2\u00B5%M's\u0090\u00C2\x04\u009C\u00A8\u00C1\x1D \u009A\u00D9\u0097}\u0081\u008Chh\\\x10\u00AE2\u00984pi\\-\u00D9~\u008E(\x17\u00BCd\u0096\u00A1b\"\u00C5s\u00BA\b\u008D)\u0095|\u00EE(]C\u00CAH^\u00EBY\u008DR\u00CF\u00ED\u00D3\u00EAQ$\u009C\u0094\u00F6~\r\u00AD\u00F1 |e?\u00E9y\u0090IG:\u00F9\u0087'\u00D9)\u009E\u0090\x14\u00FA9\u0093\u00C5\x04\u008C/\u00A7\u00A4\u008F-\u00E3\u00A3\u00E2r\u00C4\u0091z\u00DF-\u00FAQ\u00F4\x12\u00C8H\u00BB\u0089\u00C3v\x00\u00A1\u0086\u00E5\u00E3\u00DB\u00DD\u00F9\x1EZ\u00B3~\x18g\x15\u00EB\u00B1\u008CSl\u00E3\\\u009B^\u00CD\u00E35\u00A9\u00C9k;\u009A\u00AA\u0090o!m\x14\u00EA\u00FE\u00F2\x05b'\u00A8\u00FA^W\u009B\u00A3\u0082\u00A2\u008F\u00F9.b\x1D\u00B3\x1B?1_\u00BC\u00E3\u00CF_\u00A6\x07>\u00FFh9\u00E1\u00AB\x16;/>\u00FAv\u00A7\u00AB\f\u008F\u00FD\u008Do{\u00F6\u00AF\u00CB\u008B~l\u00FFl\x13\u00AB.\u0094;\u00D2FX\x04\u00E2L|\u00F7f\u00E1r\u00A5\u00AD\u00A0\u0085<`H-\u00A1(\u00E1s\u00FC\u00D7\u00ED\x0BI?\u00ACz\u00BC\u00E6\x7F\u00EBP\u00B1\x12*\u00C9\u00BD\u00D3B\x1CP\u0097\u00B9\t\x10'x\u00C2\u00DFT\x0B\u00E0\x16\u00E8p\x1DJIv\x02%.s \u00AA\x04\u00F3\x01\u00B2\x1F0X\u0089\u00D2:\u00F7U\u0088x\u0085\x12\u00B0\u0094\u00C9\u00DF\u0084\u00AF\u0094\u00D4\u00DE\x187\x10(\u00C5\u00A5ZQeR\u00DA\u00C3\u008D\u00A5\r\x11\x06\x1DR\u00CF\u00E1\x7F\u0098&\b\u00EB\u0093\u00F9X\u00F0N\u00C2t\u008B]\u00E0\u0082vD\u00C2\u008F@a|dB~P\u00CBx\u00A9\x12Q\rb\f\x07\u00D5+M\x0BId\u00B4P\u00D2\x07\u00DDfNF\u00A2sJ\u00F1\u00A2\x06\x043\u00B6\x0B\x15,\u00F0\u00E2\u00EA\x11U\t\u008FgH\u00B8\u00EF6\x0EWIb\u00F3[\u00DA!\u00AAJ@B\u00E7\u00F3\u0087zP&\u00A6\u00AA\x11aK\u00D1`4\x03\x04\u00B8\u00EE\u00DA\u00D3\x0E.\x16\u008B\u00E7\u009F\u00FF\u009E\x0Bq'\u00B6;\x15!\\\u00ADW\u009D\u00F53[\u00FF\u00E6\u00D9{\u00E77\u00E3\u0090\\\u00EF\x0E4\x05\u00B0G\x14\u00BB0`\x1D\u0096x3\x13C\x0B\x16s\u00D2\u0083A\x00S\u00B0F\u00B3Q\u0087\u00E7@\u00C4\x12\u00F0\x00\x18ihj1\x06\u0090\x19D-`\u00BA\u00EB\x1C\"t-\u009A\u00E4\x17w-\x02\n\u0096J\x1F\u00FC.44j%F\x1F\u0095\x11T\u00A6+\x19\u0081\x0Fe:x\r\u00B7p\u00F7\u00E5\\\u00F4\u00EF\\\u00CD<\u00C6\u00DE\u00AA\u00FF\u00DD\u0089^\u00F3Nv\u00AC\u0095\u00FB\u00D8\u008C\x06yN\x02_lB#\u00F7S\u0090\u00CC,F@\u00E9\u00E6\u00BF\u00A8\u00AF\u00DD@\nH\u00B1Z\u00B3))\u00C1\u008B\u00FF=\f\u009D \x120\u008FBTb\"qNR\u00AF5\u0098\u009C1\u00A2\x1A\u009D\u00C8\u00FE\u0086\u00F2\u0099\u00CF\u00DB\x1D\u00BD$\u00E7!+\u00A8\u00BE\u00A7@\\\u009A\u0088\u0083\u00E5\u00D32\u00EF\u00C5v\u009B\u00AE\u0088!\u0099W\u00CFk\u00F9\u00BD\u009E\u00A7\u00FE\u00DE\u00D5\u009D\u00DC\u00EA\u00F1\t\f\u0080\u00C2\u00EA5\u0085)\u00D6\u00B9\u00F5\u0093\x03\u00EC(0\x1B\x0F`\u00F3\u00E27>{\u00F3\u00D27\u00BEn\u00E3QO\u00B9\x12wR\u00BB\u00D3\x18\u00C28\u008E\u00F2\u0082\u00AD\u00E7\u00BE\u00E4\u00EF\u00E6\x17\u00AE/\u009AW\u00C4=\u008A~\x14f;\u00B8\tK\u00AC\u0089\u00A1\u0085Z:mA\u00A9\u00EA\u00FFI\u00CD\u0091\x1E\u00FF\u00EA\x04R41\u00F2\\\u00CA\u0080\x01\u00A6&\bf\u0098\u00D3\x06\u00A03LU\x02\u0097\u00D2\u00FE\u00D7\u0089\n\u00C2\u00E8f\u00DC\u009F\x06\u00C0\x0E\u00A0\u00EB\f\u00CD!\u00BF\u00A5d\u009B+R\u00A4\u00EC\u00B1P!\u00AA\u00A6\u00B1\r\x00\u00B4{\u00A4\u009FL\x19\fd\x06\u00ED\x1DQA\u00D9\u00F5\u00DC\u00C8] \u00BBc\u00E0Q\x18\x18\u00EB\u0084\u0096\u00F3}\u00CEr\x0Bu\x12\t\u00E7\u00DE\u00C3\u00BD]\x1AUo&\u0085X\x0B\u00B4\u00C4.z\u00B9\x1A\u00C82\u00EC4\x14\u00BA:@f&\x00\u0098?\x01&$y\u00DF\u00A2\u00CE\u00F3\u009C\x00v \x07\u008E\u0094L\u00B3\u0097\u00CFD\u008D\u00B5\u00B0\x0E\u0099M\x12\u009C\u00B8\u0084\u00AE\u00E6K\u008B\u0094d\u00D7bN\x035\u00C5\u00D3J\u00F4\x17OE\r228\u0092\u00DFc>*c\u00D7r\u00CC\u00D7\u00816\x00\u00BEq\u00AD\u00F2=\x10\tHv\u00ED*\x16Yz\u00DE\u00D7\u00C6\x7F\u00E2\x17>\u00B1~\u00F0m/\x7F\u0089\x1E\u00B8\u00E1{\u00E4\u0084S\u008FN\x02\u00AF\u00B4;\u008D!\\\u00A4o\x7F\u00C6Kq\u00DE\x13\x17mt\u00B9\u00FC\u00A5\u008D\u00EF \u0080[`\u00B5\x15\u00D7\u0094\x1A{\u00A9\x1F\u00A4fN\u00EBPw72\x1B\x12\x1E\u008DH\u00DB\u0082I\u00F3\x063\u00B2)\u00E6P\x190b\x0E\u00C1<\u0082_\f\x11T\u0083VZ\u00E3G'*\u00D1\u00861`,\u00F7[\x10tF.\n3\r\u00FC\u009E\u00DA\u00FC}K\x10p\x1A\u00A4\u009C\u008D\t,\x0BO\u0080\u00DC\u00D8\x05\u00A0e;\u00B6k\u008B\x05V\x10\x01\u00FB\u0093\u00B2\u0088\u00B8\u00B0\u00C85\u00E9\u00EB\u00E7\x16l\n\u00B0\u00A4\u00BDAz\"\u00D2\u00BA\u00E8\u00BC?Z\u00D0\u00C3\r\u00C7G\u0091X\u00E7\x14[\u00CA\u00F8\x01\u00B8\x1F\u009F\u00CC\u00A7\u00C0nk\u00FE\x1C\u0082\x18w\x12>Q\u0090F\u00FF\u00E1\u00C5U\u00B8*\u00A5EpV\x06\u00E8\u00F51z\u009B\u00C2y\u00A2\x0E\u00A5\u0094\u0095\u0098\x16a\u00DDu\u00E5\x1E\x0EH\x06J\u00A6\x14\u00CC\u00C8\x11\u0094(\u00AC\u00DC;%}\u00F7q\regm\u00A2\x1C\u00CDg\u00ED\u0092\u00CC\"\u00F6\u00A8(s>)9\x7F\u00C7\u009A@1\\\u00F2\u00F6'n\u00BE\u00E5%\u00CF\x00p\u00DE\x11]|\u0098v\u00A70\u0084\u00EB\u00FA\x17\u00EF\u00FF\u008B[\u00BF\u00F8\u00C2k\u00D6?\u00F3%\u00B2\u0081l\x02\u00E0V\x00\u00BB\u00B4a\x1D\x1D3\u00A1\u00F4\u00CF\x05\u00D0\u0080\x02%\u0093\x0El\u00BD\x12\u00B8[(\u00B2`\x06\u0088%)\u0089G\u00AAu\u00C9(\u00C4\u00B4t\u00DBw\u0093i)y\u00BB6,\u00C5l\x10<wBX\u00E5\x7F\u0090\x06fDr\u00B9G\t\u00B5B\x18\x1D\u00E2\u00E8\u00B8\u00A1(7\u0091\x19\u00C9\u00B5(a\rG\u00CC@\u00CAP2\t'V\u0095\u00E9y\u00A2\u00C9\u0084\u0094k^b\u009D\u009A\u00CB\u00B1O$}\u00BE\x03\x12~\u00D1\u00F3\u0085\x05N\u00A8'\u00BB\u00E4\u00E5Eua\u00FB\u00F1\u00D8+\u00A1\u00CA9-\u00E7\u00C65\u0096\u00BB\u00B0J\x18\u0081\u00C8\u00E1\u00B1\x0F\u00CC\u00D5\x10^\u00EBs\x13\u00B0\u00BF2P.\b~V'\u00E4)\x1C7\u00F2\x1A\x0B\u00A1[\u00DF\u00A6\u00DA1/\u00A1>\u009F\x1B\u008D\u00B5\x01\u00BAD\u0098H\u00E39\x15\u00901\u0088?\u008D\u00A0\x1C\u008E\x0B\x04H\u00B8\u00CCw\x06\u00EF\u00DD\x1E\u008BP\u00ACm\u00ED\u00C3\u00FE\x0B\u00CF\x7F\u00A1\u00DEt\u00E5\x1B\u00E4\u0094\u00B3\u00F6\u00DC\u00E6\u00E9w\u00A0\u00DD)F\u00C5oy\u00F6\u0099\u00CF\u00FF\u00DD\u00E1\u00F7\u00BEw9l}\u00C9\u00C8\u0080-\u00C2C\x05X\x070\x17/v*40j\u0098\u00F7X-\u00B9z\u00BC\u00F9m\u00C0\x1C\u00A2s\fX\x03d\u008E\x06n\x1C\u00DF\x1C\u00DA'\u00A4\u0096\u00E8\x01.\u00E7\u008D\x014\x1D\u00D0\u00E8\u00BE\u00E39\u008E\x12Z\x04\x10\u00F1~t\u00AF\u00F9\u00B9\u00D2\u00DC\u00D8\u0098\u00D7\x00\u00F4\u00EFs\u00C7\u00E74\u00E0\u0089G\"\u00DA\u00F9\u00C5\u00855qw9k\u009C\u00C4\x01\u00B0\u00FF!\u00C6\b\u00BF?&\u00A8\u00C6\u009E*~W\u00B2\u00B5\u00EA\u00E2\u00F4\u00F1G<\u0085\u00DB;\u00C2\rI#f+\u0092^\x12\u00E6\u0086J\u00E1\u00E8\u00A5DuJ\u00B8M\u0081\u00AA\u00FAX\u00FA\u00B5\x11\x19\u00B8\u00FD:\u00EC\u00DFF5-\u00882\u00E3\x14r\u00B5p(I\u00E0t\u00EBN\u00EC\x0EZ\u00C6\u00AB\u00CEP'(\u00A0el\x02\u00A3;\u00A3w2 )\u00E7\u0093q\u0092)\u00FA\u00C8\u0098\x19\n\u00A4\x1A@\x04\u00E5\u00EFN\u00E2>\u00D8\u00C9\x1Ci\u00A7\u00E9Ts\u00C4\x02\u00A2F\u00AAa\u00A9\u00A2\r_\u00BC\u00FE\u00C4\u00ED}\u00DB\u00EB\u00BF\u00FD\u00D7\u00FF\u00EB\u00CDw\u009C\u00C2\x0E\u00DD\u00DA\u00ED\u009Fr\u00DB\u00ED\x1F\u00F4cg\u009E\u00D7_\u00F9s\u009B\u00B3}w\x1A:\u00A8\u00EDV(n\x05\u00B0\t\u008BM\u00D8V\u008BI\u00E8\u00EA\u00F1\tb\u00DF\u0097\u009A\u00A1\u00CC\u00A33\u0093\x0ES\rF\u00CC\u00B1\u0088X\u0082\u0099\u00C7\x15\u00CC\u00D0\u00A5a\u0094\x01\u00A3\u00D0\u0097M\u00A16\u00B3\u00ECF\u00CF9P\f\u00A6\x16\u0088\u00A9\f\u008C\u0088\u00E4\u00BD\u0098\u00D7`\x7F\u0083\u00E7<\u00D8_\u00C4A\x04\u00E1f\u00AEBz!$\x18R\u00AEl\u0093\u00E4\u0096#\u00C0`&\u00DA\x02<F\u00BF%\u00B2\u0099\u00C61T+\u008AD\u00FF\u00D3\u0080(\x1E/)\u00D8q\x0E\u00CF+}\u00FB\u00EF\u008C\x7FPa\u00EEB\u00BAg%\u009E\u00A7x'\u00D4\u00C7\u00A5\x19\u0097\u00A1\u00931\u00D6\x7F[\u00B9\x7F\u00F9\u00C3\u00CA\u0098\"\x7F\u0083\u00B1\f~\x1F2\u00A9\u00959\u00B0V\u00ECC\u00BAz^\u00AAaL\u00E5\u008E\x1C\b\u00CE\u0081\u00AE\u008E\u00D7\x19\x1B\u0099%\u008FM\u00EC\x04\u009C\x03\u00FB\x0B&Z\u00FB\u00AA\u00DF'\f\x1F\t\x0Fn\u0087\u00B0\x06U\u00E8\u00A5o\u00F9\u00B9\u00F1\u0083\u00AF:\u00F3\u00B6\u00CF\u00BC\u00FD\u00F6%\u00AB\f\u00E7o\u009D\u00FF;\x17\x0E\u00EF\u00993d\u00F8\u00CEl\u009C\u009A\u00BD\x10\x0Bk\u0086o\u00C3\u00A6\u00BEc\u00A2\u00D6\x18\x05C\x10\u00A3\b\x06\x15\u008C\u0098a\u00C0\x1A\u0096\x1E\u0081(\x18\u00B0\u00C4\fC\u0091\u00A0\u00DD=\t\u008CAXj\u0091\u0082\u00FEoS)v\x062\u0082D\n,\u00EF\u00A6\u00E5\u00BB8\u00E1'\u00AA\x00\x02f\u0092\u00F0(\u00F9]\u008F\u00B6\u00A2\u00AE\x02\x0B\x1C2\u0083\\D\u00E0\x11U\x14\x1B\u0082\x04\u00E4,\u00FAz\u00A0\u008D\x0E\x15\u00F7\u008E\u00F8\\M\u00D0\u0084\x12BK\u0098\x16\u00A0\x16\u00FA\u00DD4\u0080\u00AD\u009B\x07\\Jz\u00DC\x07\x04\u00B1\u00FF\u0082\b#L]b\u00D1\u00C5\u00C8\u00EB\u008A\x14\u00A7}C\u00E3\x1B_2\u00E7\u00DC\u009FS,t\u0098u\u00AD\u00C2\u00D7F{\u00C0\x04\u00EE\u00FB=z\u0095\u00DE\u008A\u00CC6\u00F4\u00CF>'\u00A0F\x11\u0092\u00BD\u0095k\x1C]u@\u00B1\u008D\u009D\x16\u0095\u0096\u00D7\x07\u00E2\u00A8\u00CFE$\u00E2\u0089W\x05E\u00D8\u0089\u0082pG\u00A2\u00C3\x18E\u00A5x\u00CF\u00A0\u008C\u00F1W\x1BFm\u0087\u00A6\u00B1\u00B5\u00EB?3?\u00F87o\u00F8\x1D\x00?x\u00C8\x13\u00EE`\u00FB\u0092\x10\u00C2\x07\u00FB\u00A5g\u009F\u0087\u00F3\u009E\u009C\u00BBW\u00DC\u00B9\u008D\x12\x7FS\u0080/B\u00B1\t`\u00A1\u008A\u0085\u00A2dC\u00AA\u00A3\x03\u008FPT\u00C5(\x03\x14\x1B\u00E8X\u00F3D#\u00F3&\u008ChX\u00AAI\u00F1\u00AE\u00CD\u00AB\u00E6\u0099d_\u00C2\u00D1\x02f\u00FEo\u00C3\x12\u009Em\t\u00BE*\u0093\u00D8d\n\u00E9\x04#q\u00D2n\u00D1\u00CAw\u00B8\x141cf\u0084\x15O$\u00B3\u00C3u\f\u00E1\u00E2\u00D4\bw\u0096\u008C\x1Ad\u00B90\x15\u0087\u009C\x02f\u00E0e\u00B5&\x01\u00EB34\u0088\u00BB[%$9\x17\u00AC6\u0097\u00F4T\u00BE\u00C2x\u00C9\u00F0\u00EC6E1B\u00D5\b\u00B6!\u00AB\x0B\u00C3\u00E6cD\u0093\u00AC\u00A9\u00E2F\u00B6\u0088\u00D6\x14\u00AA?\u00B0g\u00F2{(w\u00DE\u0085\u00C0jQ\x02\u00DC\u00B7\u00A2\u0081\u0090\u00DC\u00A0}\u0087\u00A1\x11{n\x1F\x0B\x03\u0083\u009A#\u009BT\"\u008Da\x07\u0083*\u00E3\u00C1`\u00E5\u00DA|\u00AE\x12\u00AD\u00C0\u00BC\"\u0082xV\u00DA\u00A8\u00A6.a\u00F6O\u0081\u0091q*LG\u00B7\u00E7\u00E1x\u00D8\nbp\u00C4\u00A1b\x1E.D\u0086)\u00EDSE\u0088\x04_\u00A2\r\u00C4\u009F\u00A7\u00B9\u00DD\u00A4\x03\u00E8&\u00DA\u00E4\u00D2\u00BF}\u00F2\u00D6\u009B\u00FF\u00E8\u00EC;H^\u0087lG\u008D\x10\x0E\u008E\x07\u00E4\u00B9[\u00BF\u00F5\u0082O\u00CC>j\x05*\nG\u00BD3\x1B\u00F9\u00E1>(\u00F6B\u00AC\u00AA\u00A1\u00B8\u009C\u008C\u00C9O\u00AE\u00D9\u009D\x19\u00A8\u00AC{\u00AC@\u00C2\u00CC\x0E\u009B\u00F8\u00CE\u0097\u0080\x06`\u00869\u00A6)\u00CAF\u0090N\u00ECB\u009Em\x0B!\u00CBm\u00B0z\x12\x1Cm\u00D0\x1A\u00C1\rh\u00D3(\u00D9\u009D<c\u008E\u00DC\x1D\u00A9\x13\u0088h\x12\u00DDBw\u00B12\u009F\u00BE\x18\x05\u0098d;\u00D6\u00B8\x02\nD\u00B1\u0085\x12\u00F1\u00F9P\u00E4vq\u00D4\u00975\u00AE\u00B5\u00FA\u0086\u009Cg\u00FF$(\u00E7\u00DAYI\u00AC\u00BD\u00E8\u00F7\u00C5\x10\u00A6\u00D9\u008B\u00DDXR\u00DF\x16 \u00B6z\u00AB\u0083\u00D3\x01\u00AA\u00CC\u0098\u00D4r\r\u00AF\u00BF\r\u00C4\x19\x1E\u0099\u009A\u00EC.ak\u00C8\u00AAH%N\u0081\tv%\u00EE\u0080\u0086X\u0080\u0092\x1Fn\u00D4\u00E5\u00A4\u00F2\u00F9\u0090\u00F3=\x19W\u00DA\x14\x18\u00BC\u0094\b\u00AC\u00B8E\u00A5\u00CCQE\r\u0081\u00FA,z\u00C6\u00FA`\u00CE\u00F9r\u00BA\fx\x19\u00F8\u00BA\u00D3\u0088\u00A9\x006n\u00BD\t\x07.\u00FC\u00CB\x17\u00E8\u00BE\u00EB\u00BECN|\u00D0Q\u00C1\u00F5\u00A3f\b\u00EF\u00D3\u00CB\x7F\u00F8\u00D5\u00F2\u00BAG/fGu\u00DF#j\u00ACy\u00FBE\u00B5\u0092k.K1Sc\x0E#hS\x104]\u00C7\fs\u00ABQ\u0080\u00E6q\x063\u00E4\u009E\t\x06\u00D3US\u00BFVm\x18\x1B\u00F3\u00E5\u0093\u00C97'\u00F4\u0091\u00FA\u00B9KK\x0Bc\u00B5\u0085>\x03C\u00A5\bBSG\x16g4\r4P\x02\u00DC\x1E\x1E\u0080I\u00E1\u00CE\u0085H)\u009C\x069\u00B35y\u00AD(\u00C9\u00FEke\u009F\u00EE\u00C93\u00A2\u00CD%\u00AE\u00C4\u009A3)J\x15\u00C5I\u00A6y\x1C\x02\x17Z\u00B7\u00E3\u00DD-i\x1D\u00E2^\u008D\x18\u0086\u00AF\u00E3\x1EFK\u00B0\u00B2\u00B2G7\u009AV\u00A3\x163\u0081\u00B2\u0086=\x07\u00A1\u0086\u00E0\u008Ad\u0081\x1CSm\x1AI\u00D9\x18E\x17Xm\u0087\f&\x120%\u00D9\u00C3\u009F#\x18\u00AA\u00C2n\u00EF3\u00EEns\u00C1\u00CA\u00CA*K \b\x1D\u00A0\u0092F:\u00CFRh\u00DE\x07\x03\u00A6\u009C\u0099\u0091\x17\u0084\x17\u00C1\u00A3E3\u00A2\u0081\f\u00D7\u0082\u00E8\u00C9\u00BC\u00CD9>B=5]\u00F8n'\u00A8\u00C3\u0099e\u00A8\"\u00FC\u00CC\u00E7+\u00AA\u0085\u00BFwn\u00C2\u00CB\u00B9#\x0F\u009F}\u00E8\u009D\u008F\u00DE|\u00DB\x1F\u00FD0\u0080\u00BF\u00C0Q\u00B4\u00A3R\x19\u00B6t\u00FF\u00F0\u00DA\u00C5\u00AB\u009E\u00FB\u0099\u00B5O\u0082\u00E9C\u0099Nt\u00D7\u00B5-\u00B1\nK\u009B\"\u00D8tMo\u00E9\u008C`[\x1A\x16\u00B2fFDI5`!\x16\u0094\u00B4\u0080`)\u00E2\u00C7\u00868Gai\u00CE\x195oU\u0091\u00BA\x0E\x18\u00B5E\u00D2\x13\u00D0\u00D0\u00B5\u00A1\u00AB\x17\x19\u00E1y@\u00E6=\u00A8\u00B8\x1Ab\x7F\u00A32\u00B8)\u00D5\u0089\f\u009Dr\u00C4\u00D0\f\u00B5\u00D0gR\"-\x10!\u00C4\u00C5\u0086\u0091(\x03\u00F9\u00BB\x1B\x1D\u0099xcp\u00D9\u00D5\x00I\u0088*Q\u00C4\u0083Q\x1D\u00EC\u00CEJ\u008F%\u00C6)\u0085[\u00985\u00D8\u00A8>P\x1D\u00A9R\u00D4\u00FF\u00FC|\u00A5\u00AD$\x1A\u00D5\x02S-x?\u0086\u00A5\u00F3\u0098\u00F8^\x16i\x0Ba\x02\u0092\u0087\u0093s_L^\x13\u00A1\u00DA\u0086\u00DC\u00C2\u00A8\u00E9\u0089E5iI&\x04\u0098\u00FFf9\u00BCz\x7F2f\u009E7\u00E4\u00BB\u00E0\u00EFJ^\u00C4\u00F7\u00C5\u00C7\u00F4\u00F7\u00C1\u0094xJpE2\u00E7\u0089\u00D4\u00F7\u00FB3\u0097#\f\u00B1+\x06O\u00E5\u00F6w\u00AE*Z\u00AF`\f\x07\u00C3\u00BB\u00D6\u00B6\x0E\u00A0\u00BF\u00FBo\u009F\u00AB{\u00AE=*\x0F\u00E2Q!\u0084\u008B\u0097\u0097<\u00ED|\u00FD\u008Bo\u00F6G=\u009A.\u008E\u00BA\u00ED\u0085b\u00B7\u0096\u00DC1q\t\u00ACs,\u00B1\u0086\x19\u00E6X\u00AAW\u00E8\u00C1\f\x03f\x18\u00A9\u0097\u00AAy\x00\u00A8\u00C7\x0BfX\u00C0\u00CB\u00A3\u00A9\u00EB\u00B5B\u00C8_\u0089\u00D9\u00EC\x0BT\x03h-\u00CE\u00CC\u00C9d\x02\u00AA\u00D6\u0087*U\x10[l#\u00A6.Q\u0084\u00A4\u00A1^\u00AF\u00C8\x1C\u0087\x1E\u0092H\u0084\u008B5-\u00D8*\u00C4\x14\u00B9\u00A0\x01\u00F8\ne\u00BCCK\u00D5W\u00E9\u00EB&a\x02\u00DA\u00E7\u0081dW\u00DF\u00A1]\u00E7\x12H\x11\u0084\u00CB\x05\u00E9g\u00D9\u00FF]\u00C2\u00C7\u00BD\u00BA\u00DF\u00A7\u00BB\u00C2TC\x1EaRXH\u00CC\u00A1\u00AC\u00B4<\x17\u00FC\u009DF\u00BB\u0094\u00C1\u00C4R\u00A6n1\u00B88\u008Dyv\u00FD8\u0091K\x19\u00FCS=\x00<\u00CFK\u00AE\x03\u00A6b\u00B0\u00A8\u00CC$\u009C\u0098\u0090\u00DF\u00DF\u0099\b \u00A3\u00DB\"+\u00E2(\u00A8\u00CB\u00E3\x13\u00D2\u0080\u00E8\u00EF\x03\x00d,9\x14n\x00\u00AE\u008C\u00BE\u00C4P\u00A8_?\u00D9\u0087\u00C2_M<#U\x11g>\u0082\u0086\u00F9\x15\u0097|\u00F3\u00D6\u00DF\u009E\u00F74\x00\x7F\u0086#lG\u00CC\x10Tu\u00F8\u00D9\u0083?\u00FB\u009C\u00EB7\u00AEu\x18tH5\u00E7.i\n\u00DB\u00A5\u00E9F\u00B0(+\u00A3\x18\x07\fXC\u00935\u00CCt\x0E\x11&'\u00CD0b\u0086&&\u00D1Fa\u00DC\u0080y\x00F\u00D0\u00AFN\x1F\u00FEDi\x00`\x1E\x0B\x11[z45\u00D1\x7F^\u00D1\u00C1\b7zQ\u00AATKsi#\u00EF\u00D7`\u00BEeg\n\u00E1U\u00D8\x01\u00DA$\x18\x15\x02\u0086*\u009A[\u00F0\u0094\x12<\x16\x14U\"\u00E4o\x023B1b\u00AF\x19\u00B3b\u00982O\u00C9O$\x07z\x16\u00C8P\x00\u0090qu~v5@5rv\u0092\u00D1\u0095\u00A8\u00C3\u00B8~\x063B\x1B4\u00AE~\u0087\u00D8iZ\x19\u00AF\u0090\u00CCJ\u0099\x02\u00DD\u00FD\u00DF\u00D8\u009B\u00D1G-\u0095\u00C1\u0098\u00AD\u00C0\u00C2\u008E\r-51U'\u008B\u00A0:\u00E3\u00B5\u00CEB\u00FD\tB\rX\u00CF\u00C2|~\u008C\u0084\u00A7\fX\"\u00D3HT\u00A4|n\u00C9\u00FB\u0090)\u0088o\u00FAjU\u0092\u00B8;\x15\u00D5\u00A0\u00CA8\u00874\u008D\u00F5\u00C1\x18\u00EC\u00A4\u0080\u00AD\u008F\u00A8\u00DA,\u00BA1\u00B3\u00F9\u00D6\u00AD8\u00F8\u008E\x0B\u009E\u00A3\u00DB7\u00BEF\u00D6\x1ExD\x1B\u00BD\x1C1Cx\u00DB\u00F2\u00A2\x1Fy\x03\u00DEpV/n\u0097\u00BB\u00BBmA\u00B0G\u00D4\x03\u0096\u00B2\u00CAQ\u00D3\x01s\u00CF#\u0098\u00C1\u00CA\u009C\u008D\n/\\\".\u00F7\u00FD|e\u0088q\x03\u00D3\u0091\b\u00EBI\u009C\u00DDa\"\x11C/\x7F\u00B4*\u00AB\u00AB\x12Le\u00EEE\u00D7\x0F\x1D\x15\u00A9Tqy4\u0080\u00C2\u00DA\u009B\u00BB\u00ABVtT.\u008E\tq3\\9\"\x1A+|\u00AF\f\u00C5\u00DF\u008E\u00D6s)I\u009Cx\x18z\\\u0089\x19(v\u00DA\u00B0F\u00F8\u0093d\u00B7\u00F1\u00C5C\t[q\u00EDY3\u00A2\x11.\u00F6\u00C8\n\u0094\t\u00AA\u0091\u00B8\x7FYM\u00D2\u009Cq\u0090\u00E0Sb&\u00FB\u00E2x\x14\x19i\u00C99\u00F0-\u00E0\u00E8^-Oa\u00FD\u00F9y\u00B1\u00E9m+D\u00C9q4\u00C4N\u00CF1A\u00EE2\u008C\u0099R\u00D8\u0096\u00F1d\u00C8\u00EA\u00F3\u00CBq8\u00EAP\u00B1\u00F3\u00C2>\u00C0s<\u00E9I\u00C9D\x15i\u00F4\u00E4\u00A9\u00E5])\x19d\u00FE\f\u00AA\x12Dm\x00p\u00E5\u00FB\u00CF\u00DA\u00FC\u00AB?\u00FE\x11\x00\u00AF\u00C5\x11\u00B4#b\b\u00B7\u00EA\u00A6\u00FC\u00D2\u00E6\u00AF\u00FD\u00DA\u00B5\u00EB\u00D7q\u00CAA\x18ww5\u00F5\u00FB\u00EE\u0085`C\x05\x1BX\u00C3\u00809\u00E6\u0098a\x01\u00C16\x18\x13g:\u00F3\u00E8\u008C\u00C2T\x06\u008F\x13\x103\u00D4u\u00D0v\u0090\u00CC\u0080\fB\u00C5\\\x7FZ\u00CEcP\r=\n\u00C6\f\\\u00CE021\x10\u0085\u00DB\x1E\u0094\u00B5\x18m\u00C1\u00D8\u00B5\u008C5pfA\u00D7\u00A0/\u00A4\u0088\u00D8\u00A3\u00F4p\u00C6\u00A4Z\u00B2\u008Cb<izk\\\u00E4\u00B1\u0080\u00AB\u00C4\u00A1k\rf\u00A8\u00F4Eg\u00C2%\u00D3\u0095c\u0092)\u00D0\u00F3c\u00F9\u00C4\u00B0j\u0085p\u00FF\u00C4\x02\u0084\x18\x1A\u00ADN\u00C4y\u00CC\x19B-G\u00C4\u00BD\x16\u00E3\u0086\x03\"\u009BR\u00B8\x1D\u00BC\u00C6b\u00CF\u00C1\f\u00C8\u00A2\x02\u00F4| \x18\u008D\u00D0c!\u0085q\u0088\x13\u00BD{J\u008CQ\x15C\u00A2\x1FWe\u009C\u00AC1\u00AExVN\b\u00FC\x19B\u00FD\u00D0\x1CK\u00BC\x1FH\u00CF|h\x00\x00 \x00IDAT2)\x7F\x7F\x1C<\u00BD\b\u00BC'g\u00D9kn\u00E6\x032\u00E4\u00DC\u00FB\x0EF?\u00B5\u00D5I\u00E9)z\x14`}q\x10\u00FB\u00DF\u00FD\u00E6_\u00D3\u00CD[\u00FE\\6\x1Ep\u0087I\u00F4\u0088\x18\u00C2\u00C7\u00FA'\x7F\u00E0\u00AF\u00DB_=bl\f\u00C79vm\u0084`\x0F\x04\u00BB\u00C4\u00EA\x1C\u00CE0\u0083e32'\u00C1\fg\u00AD\x18\u00EBf\x10c\x10\u00C8=\x16\u00AC@\u00AB1\u0091\x11\u00E9\x11\u00C8\u00FA\x04<f\u009F\u00C7X\u00E0R\x00/\u00E2Md\u00C0\u00921\u00A4\u00D0\u00FF\x1D9\u00B0\u00CA\u00D3\f\u0082\u0081Y\u008F\u00D5_\u00AE\u00E2{\x01\u00B4\u0082\x10\\\u00AD\u00E0\u00FD\x1A\u00DC5V\u00AC\u00FC(\u008CB\u0090P\x14\u00F9\u009B\u00AAB:\u00DD\u0096\x1A\u00BF\x1B:\u00F1\x05\u00C8 \x1F\u00C2__\u00BC\x11\u00DE\u00CFE/.\u00F5\u008A\u00CBW\u00D1\u0080\u00D6c\x1Cy<%\\\u00F0\u0083\u00C2{\u00F2\u00FA\u00CA\u00EC\u0090D\u00EEscS(\u00C9T\u00C4\u00C4Q\u00E8\u00E4\u00A1V\u00A1\u00F4SB\u0096i\u0083\u00C12\u0099\x02\x00f\x1F\x16\u00A3K\x19\u00D8*j`\u00BF\u00C5\u00E5\n\u00F3\x10\u00E5\u00FE\u008E\u0095<\u00CBg\u00AA\x0F\x15!\u00C5\u00F7\u00BE\u00D2o\u00B9'\u00DD\u00B5\u00DEO0\u00DA\u0095Qq\u00B8\u00CCg\u0099\x7F\u00E4\u0092Gl\u00BE\u00F5U?\x00\u00E0\u00AFWO;\\;\"\u0086\u00F0\u00EA\u00CD\u00D7\u00FE\u00D2\x17v}z\x02\u00BD\u008E]k8 k\u00D8\u00A3s\u00CCd\u00869\x1A\u00E6\x180s\u00A8O/\u00C2\u00A8\x1E\x13\u00E0\u00DF\u00850^\u008C`\u00C3=\t\x16S\u00B5z\u00CC3\u00A4=\u00A0*\x1C\u00F4H\u0088\u0087(CX\u008E\u00ADM\x18\u0087@0J\u0095\u00E3l\u00CC\u00A5\u00ABe\u00C1\u00EDy\u00F2\u00AF\u00C0X\u00BF\u0086\u00E7\b\u00A6n\u00CEC.< \u0098\u00CB\u00E4\u00FA\u00D2'\u00A5\x1E\u00C7\x1A;9\u00C5@\u009D\u0089q\x0B5Bog&\u00C9\x07\u008A\u008A\"\x15\x0Ep1S\x15J\u00C8\x1D\u00DF\x05\u0098\u00D6\x13 %\u0093\u00C0c\u00A0S\u00E6P\u00FA\u00B1\u00CAHb\x04\u00C9\u00B4n2\u008D\u0089\u00C4\u00ADh\u0096(\u00CA\\\u008F\x10xl\x1D\u00E7\u00A3>W%\u00E0\u00AA\u0096\bl\u00AD\u00AC\x14\x0FV\x1ASW\u0086\u008BJ\u00F0\u00E5\u0084>\u0094gc\u00FF3djt\u00B9\x07U\x03E\u00B9o\u00B6:C\r\u0082\u00F5\u00AD\u00FD\u00B8\u00F5\u00A2\u00F3\x7F\tG\u00C0\x10\u00EE\u00B0\u00DB\u00F1\u00E3\u00FD\u00EAG\u00BEux\u00D3\u00D9KYB\u00CB\u00FF\u008EE3\x0B\u00FE\x00\u00C5:\u00F6\u00A0a?f8\u0088\x01\u00DB\x10l\u00A3a[\x1A\u00B6\x01l\x01\u00D8\u0086E$.\x01l\x0B\u00F7o\u00F0hEa\u00F8\u00B2\u00FD\u00D1\u009E\u00ABJ\u0097\u00A2\u00AB\x12d2\u00A8\u00C1K\u00B5fB\u008D^l\u0081:\u00F2x+\u00E7\x0F\x18\u00DD\u00DB\x11\x05R\"\u00B1I\\j\t\u00D0\u009C i0\u00EC\u00EE\u0092r\x03\u00A9%\u00FA\t\u00CCHg.B\u00EE\r\u00D1\u0099\u00F3\u00D0\u00FC\u00CFU\x17\t\u00B7\u00A0\u00F5),\x0E\x02\t\u00F7&\u00C2\u00AB\u00A7N\u0093\x19\u0089g\x11\u0084\u008C\u00A9\u00F0\u00F14\u00AB\u00C0\u00C4\u00F8\nV\u0084b\u00A2T\u00BAS\x19\u0088\u00D5\u00C2\u00E8\u00CB\u00F8~i\rh\x12\x02^buq\u00FC\u00C0D\u00AD a\u0085\u00EE\u00EF\x7F\u00F1y\u00C8\u00F9\u00EC\x00m.dzT\u00EF\x10\u00EB\u00C8\u009F\u0085\u00E4P\\\u0083\fI'z\u008A\u00F8\x01\u0097\u00DA\x1A\u00E7\x0Bv\u00D6\u0093l\u00D8\u00C1\u0094\u00E3\u00B3\u00C4\u00DC\x07\x02,\x11\u00A8\u00F9\"xIA\u008C\u008C\u008E\u00E4\u00B9\u00EA\u00E3\u00AA\u0081Sn\u00BC\x1C>|\u00F9\u00D9\u00E3\u00BB^}\u0087\u00EB/\u00DEa\u0084\u00F0\u009A\u0083\u00AF{\u00D6G\u00D6?\u00E4\u0093w\u00D7\u0084*\u00DF\u00B1F\u00AEi\x05D\u00972\u00C7\x1E\x15l`\u00C0L\u00E6\u00B0\u00C0\u00A1\x19\x06\x190x\u00CA\u00F2\u00B6\u00FB\u00B3\x1B\x06,]\u00CF\x1EP\u008D\u0084%\u00B6\u00C0\u0089\x19\x11b\u00EC/\u00CB?\u00B7\u00A4\u0098\u00F2?#\x06\u00BB\u00D6j9u4,]M\u00D1bl\x14HT\u008B\u00EER\u00FAP&\u00D3\u00F8\u00BD\u00B9(\x14\u00BE\u00D8J\x04\u00A2\u0087\u00AE\u00DA\x16WC\u00AA\u00B3\u00CD\u00B3\x02C\u00FAJ\\_\u00AB;\u00D9\u00FE\u00D9U\x1E\u00DAB\u00CA\u00FD\x1E\u00FDwJ\u00FBf\u00C4\u00A4]3.H\u00D2\u00A1G_\u00F8\u00A1\u00E5\u0083\u0093\u00B8 \u00A1~Qv\x15\u00D5\u0080\u00D6\fvG\u00BEB\u00CFc\u0085\b\x01@\x19\u00A4t8\u0099\u00E4\u00E1\u00DE\fr\u009A\")\x1F\u00B5\u00D8;\u00B0@\u00DB\x15[\x06\u00EF)\u00E4'R\x0EW\x14\u00B4\u0082\u00CC\u0082\u00C9{\u00B5\u00A4\u0089)\u00B9\u008Ew\u008Ax\u00D4\x03\u00CCRm\u00C9\u00B9\u00CBHF\u00AEI\x18\u00BA\u00D1j[\u00C8!e\u00AE\x06\u00B0~\u00CB\r\u00D8\u00F7?\u00DF\u00F4,\x00?y\u0098\u0099\u009A\u00B4;\u0084\x10>\u00A7\u009F9\u00F5\u00A2\u00E1\u00A2\u00A7\u00F5\u00B6<D\u00B2\u00C5\u00DD\u00DD\x1A,P\u00C5j()\x1A\u00F6\u00A3a\x0F\x06\x1CD\u00C3\u00A64l\u00A2a\x1B\rK\u0098\u0091q[\x04\x0B4,\u00D0\u00B0\u00D4!*7/a;D-1\u00D81\u00FF\x1Ba\u009E\u0083Q%\u00BE\u00DB\x06\u00B3\u00E2N(\x1A!3\x16>\u00AB0\u00F38\x02Ce\\\u0082\u00EC@\x12,\u00E3\u0082\u00F8\f3 \u008A@=?\u00A03\u00AB\u0090\x01+2\x00\u00ADAZ#]\u00D9\u00F9\u00B1x\x1A,\x04\u009A\u00EAAap\u00DE\x17\x19]\u0081\x04N\u009C)\u00BD#8F\u009A#\x11{F\"+\u008D\u00BE\u00AC\u00D8LV\u008F\u00AE\u00FD\x16U\u00A8d;\u00C6_0\u00AB\x1A4\u00C51\u0094\u00B1\u00C0\u0088\u009B\u00D7\u00B115|zO\u009F\u0094*\u00A9\u00CBsR\u00C5\u00D06C\u00A8\x07\u00BC>\u00E2=*\u00E3\u00F79.\u00EF{\u0095\u00A0\x03\u00ADL\u00A4\u00BB\u008F!\u00CA\u00B4U\x02'\x1AA9\u00B7\u00F1e\u0096\u00E7\u00A9\u0081JD\x00\u00F5y\x15\u0099FM\x04\u0091c\x12\fh\u009F\u00FC\u00C0\u00D3\u00F4\u00AA\u00CBO\u00C5\x1Dhw\b!\u00FC\u00ED\u00F6\u00C5?ua{\u00D7F\u0096!96\u00CD\u008Cj\x1E=\u00C7\x04\x19X\u00C5\"C\t\u00CD}\x0E\x19\u00C1\u00D8\\j\u00E7&.$\u00CC\u00C1\u00A59\u008D\u0089\bb\u00A6\rB\u00D4\u00E0q]\b\u00A6B\b\u0096\x0E\u00E1\u00E8A\x10 \f\u008E)u\x1D\x11\u00B8[\x12\n4\u0087\u00EC\u00A3/\u009C\u00EER\u00D8\x02\u00AC\u009C0Cof\u008C\u0081C\u00C4\u0080\u00A5\u00BE\t\u008D\u00B2\u00EC\x1A\r\u00A1@\u00C1\u00DD\u00DE\u0097\u00BA\u00F7.%\u008F\u00C8\u00E0k\u0089\x12\u00DB\u00A0u\u00F3\u00EB\x1DX\x17\u00C9\u00ED\u00A8#\x10\u0093\u00AD\u0082Fh\x1A\x02\u00D8-\u00FF\u00F5\u009D\u00B5\u009Ee\u00D1\u00C4;\u008B\u00BF\x0E\u00D1\u00B9I\u00BBncQ\u00D7\u00FFm\u00F8\r\u00DA\u00DDU\u00E7\u00AB^\u00D0l3Z\x12\x1F\u0091\x07\u00FC\u00DF \u008AUi\u00EF\u00CF\u00AD<\u00DF\u00F2\x1D\u00C2\u00DB\x02\u0084qR\u00DD0\u0087.\u00E6\u00FB\u00F7\u00AEl\u0095\u008C\u00D0\u0088_h\u0085\x10]4\u00A3\u00C16m\u00B1\u00F1\u0087]#\b\u00DE\u00CFS\x18Qw\u00D8<0N\u0082\u00E7\u00F0\u009A\u00E2\x16\x15\u00F5\u00FE\u00B8\u00DB\x16\u008D\u00C0\u00BD\u00C73\x11aj\x17HW\u00B4q\u0081\u00F5O\x7Flc\u00FF\u009B\u00FE\u00F8\u00A7\x00\u00BC\u00E00\u00A4\x15\u00EDv\x19\u00C2\u00D6\u00F2@\u00FB\u00D7\u008B_\u00F8\u00E9\u00AD\u00B5\u00BDH\x7F\u00F7\u00B1jU\u0082\f\u0093\u00BFmi\u00B8Q\x15\u00EB\u00DA0\u0097\u0086-\u00D7y)M\u00A2\u00DC\u0089\x13\u00961\u008A\x04\u00D1\u0091\u0081H\u00E2\u00F2\u00F3\u00A8\x0E\u00D4\u00C2 \u00EA\u00C46\u0080\u00B6\x06\x7F\tN\u00B0\n\u00EE\x1C\u00E8\u0088@H\x10\u008C9K)\u0093\u009E\x02\u00C5\x10\u009B\u00C6\x12\u0099\u00B2\x10k\x03\x03\u009D\u00D2Ah\u008B\u009D\u00C7\x03\u00E2\u00FB\u00FAa\u0090S\b\u00A0p\x11\u00B8z\x000\x04\x01\u0088s\x10\u00C7\x11gec\u00E4\u00BCS\x07b\u00B7e\u00EA2A\x1C@\u00D8\u0097\x18OP\x02\u009BHaY-\u00C8]\u0080\x11}\u00C8-\u00D58\x1E#B\u00EB;\x0B\u00BA\u00A0\u00E4E\x04\u00C3!\u008C.\x1B\u00D1N\x1F'\x1F:B\u00A4\u00C3\u00E0gk,\\\u0084\u0084\u00FB\u00CED\u00ECJF \u00FA\\2\u00DD\\\u00BB\x07Ka\u00F2{DG\x06\u00AA\u00F1\u00BE\u00AB1\u0095\u00EE\u00CBp3R-(\u0088F+\u00B3@\u009E\x17\u00C6S\u00FF\u008Dh!fE1h\x07>|\u00E9O\u00EB\u00DE/\u00BEH\u00EEw\u00F2m\u00EA\u00FB\u00B7\u00CB\x10\u00AE\x18>\u00F1\u00A4\u00BF[\u00BC\u00F5\u00F4\u00DE\x00\u00A0\u00EF\u0098\u00E3\u00BB\u00AFQ:\u00CE\x01\u009D\u0081\u00A9\u00BB\u00B99\u00EA\f\x0B\x01n\x15\u00B3.\x18A\x0E\u0098\x17\u0083\u00E0\u0082\u0084\u00AA\x1E\u00B4\u00E4}2\u008Dut\u00A2[B\u00C0-\u00E0\x1A\u00EAn\u00D3\u00FE\u00AA\u00D4<\b\u00CB\u0088\x104\u00F7\u00E6\x00\x17dRC\u00953\x17\"\u00D3PZ\b7[BL[\u00CE\u00BA\u00C7\x11\u00B5X\x16\x14mF\u00CCW0\u00B4Rc\b\u00A8\x1Ad\x16\u009F\u00F9\u00D4\u009B\u00AB\u00A1I$<>\r \x00i}:\u00EFQ\u00E3P\u00DD\u0080\u00EF\u0096tqw%\u00D1\u0086\u00EAd\u00B9F?\u00E2\x01J<&d-\n\x0B\u0097\u00E4\u00D8\u008C\u00E0+\u00E1\u009A\u00FA\u00E4\u00DE\x00h\u00CE\x0BLR\u00AA\u00BB\x1Fm\u0088C\u00DC\u00DD\u00EE\u00C5 \u00A2\u008E\u00F0\u00FD\x0B\u0090\u00AEK\u00B7~T\u00C3\u00DF!9e\x12\u00A5\u00ACfY2\"\u00B1\u00AF\u009C\x1B\u00AB\u00A5\u00A0\x178\u0093\u0088w\u00CE\u00E1\u0092q\x14\u0084\x03gT\u0093\u00EFd4dF<V]\u00AB|\x17\x1C\u008F\u00A1\u00BB\u00E1\u00C3\x1F<}\u00EB\u009D\u00E7?\t\u00C0[q\x1B\u00EDv\x19\u00C2_\x1D\u00B8\u00E0\u00DC\u00CF\u00AD\x7F\n\u00C7\u00CA\u00A3\u0090-\u00E3\x0B&\u00BB(\x17i\u00DB!\u00B8\x19\u008A\u00B96\u00CC\u00BC\u00E8\x063\u00D7\x07\x15\f\u00D2\u00B0\u00D4\u00C8\u0096\u008FpUb\x00\x06+\u008D\u0092\u00B6\u00F4\u008E\x16D\x1D\u00DF\u0085F\u00C3|\u00F9\u00E6\u00AC\u00E4&\u00B0\fE1o\u0086p\u00FB\u00B5\u00E6\u00EA\u00853\u008D\x0E&#Y7m\u00B2\u00A0\x04f\u0085\x07ha6!\u00CB@\x19;G\u00DD\x00\u0099\u00B1\u00F3\u00D6\u0099]\u00A7\x11Al\u008Bgp:3\u00A6\u0091&\u00A96]\u00C7\u0094\u0080nL\x14\x1D\u009C\u00E2\u00B8\u00D0lQ\u00A6+\u00D1\u00CEW\x12\u00B8z\u00BF\u00CCk\u00F0p\u00E90\x12v\u0089\u00F8 \u00BB\u00B4\u0085!\u00CC\x1Em4\x03\u00A7\u00C0\r\u009D\u0083=\u00B5\u00AA1\u008E\b\x02\u0082\u00ABt\f\u00A1\u0097\u0082\x12lL\u0089\u00A5\u00EA\u00D4:\x1AQ\u00CFbd9\u00F8|\x14\u00FE'\u0099\u00F8\u00A4\u0093\x16\u00CFaII\u00EA|\u0095\u00BFs\u00D2\u00F3\u00A6\u0096\u00DA>\u00C6\x1C\n\t\u009EHG\u0099\u00B3A\u00A9\u00AFN\u00E3\u00BD\u0080\u00AA\u00D5~\u00BD\u00BE$#\x1C+\u008D\u00BA\u00AA\u00C8\u009A\u00A3\u00EB\u00CB\u00FD\u00B8\u00E5\u00C27\u009D\u008B/\u0085!\\\u00A3\u00D7\u009Dt\u00EE\u00E6\u00B9O\u00EDm5\u0093\u00F1\u00EEW\x1C\x04\u00BE=\x19\u00B7>\u008F\u00B7\u009B\u00C6 \u0095\u0086\x05\x06|\x11\x03v\u00A1\u0099r!3\u00CB\u00CD\u00F3\u00AAI\u0083\u00DB\x06b\x03\x14\"\x044\u00B0\u00B8i\x0B\u0084\u0090\x0E\u00B6\u00E0\u00C3*\u008E$\u00CC\u00E0\u00A3j/\u00BBF;\u00DA\u00B9C \x03\u00AB\u00C8l\u00E3\u00ED\u00A0{\u008D9\x11\u0088gY\u00F2\u00EEb(fbd\u00E2i\u00A17\x0B&\x1B\u00BF\u0086[\u008D~\u00F4B\u00E1d\x1E\r\u0099\x0B\x00\u0097\u00AE\x15\u0089D\u00D3\u0089\n\x10\u00D6mF\u00FD)-\u00FCIx\u00B9:8>\u00EFg\u00D2\u00AF\x13\x06\u00C7O\u00A9\x06M\u00A9\x1D\x1D\u00D5<\x01B\u00EC\u00ACM\u00C0\u00E4&S\u0097\u00B4\u009C\u00EB\u00F7Q\x14\u0086UU\u00AD\u00D2\u00BF\x02\u00CAd#\x12\u00DFd\u00D1\u0089?\x7FM\u00BEb\u00AA\u00B2\u00A3\u0096\n\u00D7\x0F\x19qE\"g\u00FF2\u00F9=\n\u00B8\x06\u00A6j\u00E5x\u0099\u00A7\u00C9q&b9\u00E3\b\u00F5&\u0091\u008D\"U\x07\u00AA8kW\x7F\u00F8\u00A9\u00FA\u00F9\u008F\u009D$_\u00F5\u008D7\u00E30\u00ED6\x19\u00C2\u00FF\u00DA\u00BE\u00E2G\u00DF\u0087\u00F7l\u0088\u00CE\x00\u00B9Sw\u008C:\u00C2f\x15\u008F\u00B8\x11+K\u00A21;P\u0095\u00B6\x02\u00B3\u00E8n\u008Ab\u008F6\u00AC\u00C9\u00CC\u00E2\x15\x15\x18$}\x01\x10\u00B5]\u00A0}\u00F1\u00B2@\u00C6L@\u00EC\x00q\u00D4\u00D0a\x1E\x07\x06%\u008D\u0092R\u00A3\x01\x101\u00CF\x04\u00B9;\u0097g\u0087y(\x06\u00B4\u00A0\u0091\u0080\u00F9HGT\u00DE\u00D1\x18\u00C4\b+Y\u00A2M`{1j\u00BA#\t\u00FF\u0095R\u00CF1\u0085\x1B\u00B7\u00B4\x19R\u00E0\x1Aa\u00B5\u00A4\u0088e\u0080\u0086kR\u0089>J\u00B3{\"U\x0B\u00A1\u00DE\u00CCPe\x01\x0B\x1D\u0087\u00FBs\u0082\x12J#4'\u00DD\x13\u00DD\u0088B\u0086\u00F2\u00A3\u00DFk\"1\u00C3m\u00D7\u0091\n[\u0081\u00F0\u00D2`%\u00D4\u00FD\u009C\x1D1\n\u00F53\\\u00AD \u00F3\x17d\u00895\u0087\u00E0\u00F5\u00BB]Q\u00FE\u00F1\u00C8N\u009A\u00F2\u0083\u00D1\x19^\u00A4*S\u00DFh\u00BD\u00F7\u00E4_\u00A2\u00B4\x18\u009F\u00AFY q\u00CC\u00AA\u00CA0Q\x1D\x14\u00C9\u00DC\u00E2\u00E9l\u008D\u0094\u00BD&\x0E\u00D5T\x15\u00C35\u009F\u00DC\u00B8\u00F5/\u00CF\u00FBQ\x00\x7Fx\u00B8\u00F3n\u0093!\u00BCe|\u00EB\u00D3\u00F7m\u00DC\u008A.U\u008B\u00BE\u00BB\x1B_d\rr\u00A9\u0093\u00E6\u00A4F\x03\u009C\u00BB\u00DFn\x11\u00C5\t\u00AE&\f\u00D20\u0087\u00A55\u00CD\u00B5\u00C4\x1C\u0088\u00A4\u00FB\u00AC\u00FC\u00AE\x1E\x13\u0090\x11\u0088TQ\x10\u00D2\u00DD\x18\u00D1,\u00DCf\u00D3\u0094ic2\u00CD\u00EF\u00D1\u008A\r!\u00AB)Q\u00EE\u00D4c\u00F463\x10\u0088l$\tz\x12\u00AB\x0F8<\u00A5A3\u0099\x1B\u00AB\x1B\x07\u00C4wv\u00C4\u00FC\u0088\u0080\u00C1&\u00FA|\u00AE)R(\u0095\u00ED\u00B70\u00A4z\u00EE\u0083m6\u0083P\t4\u00A4\u00A3&\u00BC\u00ADR\u009F\u00F7\u0086\x16T\u00E1\u00C3\u00EA\u00E5^\u00E1?K\u00F4!A@=\u00C7\x05\u00D7/te\u00DC\x05N\u00A7\u00BF>\u00F36R\u00F7N\u00C9\u009E\u00DB\u00B7\u00AD\u00A2`#D\u00FA\u009F\x02\u00B2+@+\x7F\u00C6\u00E4Ti^\u00A4\u00BDq\u00D1r\x1FC9\u00E6\x1D\u00A0\u00FA@\u00A3$\u00C7\u00E1\u008CC\u00810pr>\u00CB\u00BA\u00B7\u0090mu E\u00EF\u008E3\x07N\u00A3??\u00A7k\u00DE\x17\u00D8\u00FA\u00D0%O\u00C7\u00D10\u0084\u00EBu\u00DF\u00C3~x\u00F3\u00A9\u008F\u00EF\u0093\u0082\u0095\u00C7\u00A25\u00E4\u008E\u00C0\u00B5\u00EA\x11an\u0081\u00D6\u0085\u00E3.z\u00C3\r\u00E2\u00FB.\u00A9:B\u0080o5_!\u00B9\u00873\u00D3\u00BD\x07I}0\u008EcBLD#\u00AC\u008A<C\u00CD\u008Bp\t\\\u00DC\u0091D\f-\u00A4\u00A4\u00F8{\u009A\x16+\u00B1:\x07\u0095\u00E1\u00A1\u00C0k\u00FF]\u00A6\x0B-\x17_e\x1E\u0088\u00B1&\x13\u0091\x102\u00A1s\u009Ae\u0091O\u0097:\u00BD\u00F0?\u0089\x00\u00EC\u00D2\u008C\u00DD'I0/B\u00BB)H\x13)jGR\u00FA\u00F5d\x1A\u00B1\u009A\x1A\"<:\u009FSs\u00C9\x15t\x13\x03\u00E9\x1E\u00A4\x15\u00DC\u0087\u00CF\u008A$|Jc\x1Da\u00A9\u00C6\u008A\u00C8W\u00E0\u00C6\u00ACT7xM/\u0088\u00A3zE&\u00C9H\u00F5\u00CF\x07\x14\u00F5\rP\x1E\u00AC0\u00AENU\u0087\b!\u0099\"\u00ED?vz\u00EEx&\u00D1\u00EF0\u00ED\u00DBm*\u0093\u00A4,\u00AE}\u009D\u00868\u0093\u00FF\u00A6\u00EA \x18>\u00F9\x0F\u008F\u00D7\u00CB\u00DF\u00F30\u00F9\u00D6\u00EF\u00BC\x06\u0087h\u0087e\b\x17,\u00DE|\u00CE\u00A5\u00B8\u00F4X\u00C1\u0082\u00D2\x06\x00\u00EB\u00B0\u0082'T\x0B\u008CP\u00E9\u00C3O\u0089@\u00A9`\u00BA\u00EAAi\u00D8\u008B\x01\u00BB X\u00D3\u00C1\u00B3!\r1\u0088\x133\u00C9\u0088\fB\u0085\u0092:\u00E3\x16F\u00CF\u0094\u00CC<\u0082V\u00CE\u0099\x02Nj\u00DC\u0095M\x13\u0094\x0E\x18\x10!\u00F3\u00B0\u00D4lH\u008F1\x18w\u00EFf\u0098\u0094d\x02M\u00B88\x04\x16PC\u00AEoz}x.T3\u00E8\u00C8\u00E7\u00A5\u0092s\u00A0\x064wo8\u00D1\u00BA$\x0F\u0095As+\u00B6j\u00C0\u008B0\u00E7p\x0F\u00E6\u00ECW\u009BB\u008F\x1C\t\u00D8\u00EFf|q\u00E3\u00DD\f\u00DDk\x1B\u00C6NG\u0085iA\u0096\u0080\u0088mQ/\n\x1DK=o\x0E?f\u00D5\u00F3\"\u009C\u0089\x18O44\x1B\u008E\u00826\x00\u00A3\u00BF\u00A5\u0089\x04w\u00A2\u009A0\u00D7\n\u00D5\u0081\u0084\u00E9\u00D3\x16\x01\u00A0$\u00BC\u00B0\u00C1tT\u0094\u0092\u00E8\u008CL\x0B\u0098\u00AA\x15~\u00A6\u00B62\u009B\u00FE,\u00AAa\u00C7\u009DVkv\u0083\u00EC\u00C4\u00C0Xb\x14z\u00BE\u00F5\u00C9\u00F8; :b\u00D7\u00F5\u009F\u0093[\u00DF\u00FC\u00FAs\x00\u00FC\u00D7\x1D\x03\u00C1m0\u0084\u008B\u00B7\u00DFs\u00CE\u00D6\t{o3:\u00F4\u00AEo3\b\u00E6\u0080\u00B8\u00ED\u00A0J\u00CFp\u0099\u00D5(?\x12\u008E\u009D\u00D7\u00B5a\u00AF4\u009C\u0080\u0086\u00990\u00A2\u00C0\\\u0091\u0083\u00D7JX\u008A\u0095Z_\u0082\u0081A\u00FE_\x157\u00EE\x05\u00B9\x01ji\u00D5]\u00C4K\u00A9\u0099ps\u00F9\x1E\u00B2p\x14\u00C6\u00C8\x0F\u00CEt<\u0091I\u00AA\u00CA\x00\u008F\u00F8K5\u0083\u00F5\x12\u00C9\"\u00FC\u0097\u0088S\u0097\u00F2\u00FCA\x17\u0093\b\u00C5\u009E\x04\x16\x11n\\\u00EE\u00C5\u00AFO\u00AA\u00AA\u00EE5!\u00CA@\u00FE6! \u00F5s|\u00F1\u0092\"\nl\u00CF\u00CB\u00C9A\u00EA8y.k\fT\t\u00CC\x11\u008E\x10\f&\u00CDc+xx?V\u0090\x154hv\u0092\x10359\u00DE\x01;\u00A4\u00BB\u00F8\u00F5A\u0098\u009C\u00A7\x01,r\"Er[\u00A1\u009B\u00AA\n ?\u00C7\u00FC\u00F3\u0098\x14T\u00E2\u00FDO\u00EA\x19\u00F8\u00F3\u00D1\u009B\u00A4\u00D9\u00C7\x04Y\u00F0;]\u0090q\u00E3D\x7F\u00C9|\u00C6@\x01Y\u00AF\u00A1\u00A2\x14\u00C5\u0084\u00F1P\u008DTE\x1B;\u00DAG/?,Ch\u0087\u00FA\u00F1\x1A\u00BD\u00FE\u00A1\x1F\x1A.}L\u008F\u009B\x1C\x1B\u0096 \u00A8nE\u00FF<\t\u00D6\u00A0\u0091h\n\u00B3Eh\x12\u00B4R\u00EA7\x03\u00D8\u00AF\u0082\u00ED>`\u00A1V\u009Bq\x01\u00DF\u00E8E\u008Dq\u00A8\n\x16RB\u009A\u00DDE\u00B9\u0084y\x15\x16\u00C2\u00D8\x03\x0Fe\u0096\u0086\u00A50\u00F4\u00D9\u00A2!\u0097:\u00F3$)\u00BAH[\u00B8}\x00\r\u00C4A\x06\u00C6d\u009F\u00F8\u008E\x06\u00AB\u00C1\u00C0Me2\u00CC\u00B9BCF\u00C9\u00E5\u00C6-\u0096\u00AA\x1D\u00B1\u008E%\u00ECX\u00A4\u0095\u0099\u00F1F\u009F\u00B94d\u00D9u\x0F\u00BE\u00F2MP\x04Y\u008A\u00CDB\u00A5g\u00906\u00C45\u0093\u00C4%\u008F\u008B\u00C8$##\u00CCI\rB\u008E=\u00DE\r\u00AB@\re\x0EL\u0085\u008A\u00E7\u00AD*a\u009C\u0097HP\u00B50! %\u00A7r>\u00B9\\\u008C\tN\x0B\u009B\x165\n\u00E2\u00F3\u009Eu\x18i\u0093\u00D1\u0080\u00DF-\u00FE\x15w%KOi\u00AE\x11\u00B1\u00C9\u00A45^\u0087\u00BCg\u008Dw\u00F0\u00B9\u00B3\rar\u00C5\u00C7\u00F9\u00BA\u00F2\u00D6\u00D8/\u00F2x\u0084\u00B2\u00ABL\u00E9\x02b\u00B6\x19gJ\u00E1\x1C\u00F1Ml\u00F5\u00EA\u008F>F\u00AF|\u00EFCq\u0088vH\u0084\u00F0\u00FE\u00AD\u00CB\u009E\u00FA\u00A1\u00E1\u00FD\u00A2my\f\u00D1A\u00F1\x1C\u00AC,\u00AA\u0086\x06\x15\"\u0083\u00BA\u00E8\x10\u00E7\u00E4\u00AEH\r\u00FB\u00C5\\\u00913X|\u00C2L\x1B\u00E6\u00F0\u009D\u00A4=\x18H`Kq\tF X\x12\x12\u00F3\x17\u008C\u00B9\u00A4\u008C\x1E}<FF\u00A6\u008E\f\u00B5\u00A0\tG,\b\u0082Q\x0F\u00BD\rP')o\u00A8\\t\u0091\u00E8\u009F\u00FF\u0083H\u00EE\f]\x02\u0095\b\u008D\u00CD\u00B6$h\u00AD\u0081U\u0094\u00F9TF4\u00D58\u00D5\u00F2\u00C6\u00FC\u00B7\u00B3\u0090\u0089\u008F:\u00F2\u00ED\x05\u00A5.\x1A\u00A0c\u00A8\x1D\u0099.\u00DDL\x15PK\u00CEQt\u00CB^,\u00E89z\x10xucJ\u00E9\"\u00CD\u00A9\u00D2(\u00D0\x18\u00B4#\x007aM\u00D5\u0087L\u00DF\u00CEQ\u00ED\u0089\u00CD\u009A\u008D\u00B7\u00EE:\r4\u00D7\u00E1}N\u0094\x03+h\x01\x05)9Q**J\u00E0q\u00A2\x01DH\u0085\u00C6\u0088\u00B0B\u0094\u00AD\\#\u0098n\x01\u00E7\u00CFD\x06S\u00DD\u00AD\u0081.\u0088,r\x0E%\u00D4\u00E5\x11\u00B9[H\u0099O\u009B\u00C5\x12I\u00D9,\u00D6\x02\u00B5\t\u00D6\u00AE\u00FB\u0082\u00EC\u00F9\u009F\u00E7?\x15\u00C0\x7F\u00C7J;$Cxk\x7F\u00FBS\u00B6\u00E6\x07\u008F!3 \u00F4\u00F3\u00D4^\x12~$xP\u008A\u0090a\u00AC\u00A0\x04\u00F7\u00CBW\u008Ey+\x14'H\u00C7\u00BA\u00D2\u00F9d5\x17\x15\u0088JC\u00AA&)c\u00C7\u00C7P%l'gi\fF\u00F2\u0080\"\u00C9\u00F0d\u00D6C`\u00BA\u00F3\u00E8\u008Ce\x04\x00\u00E1\x1E\r\u00F4Z\u00D83f\u00D5&\u0080L\u0084\u009E\b\u00EA\u0085M&\u00EC\x05N%\u00C8\x04\u0097l\x1Dno\u0090\u00B2\x10\u00FD^\\\u00B8\u0091\u00E8T\x16\u009B\u00D9!\u00D4\b\x14(\u0090;.B\x12\x10\x10*G\u00FC\u00EE\u008C\u0085\u00CCj\x02Y\u00B5tT\u0089\u00A2\u00EA\u00D6\x0E\u00DB'>w\u00EF/\u008Cg<\u00B7\u009E#\u00C6\u00A4xn0\u0097\u00D1\u00CF*\u00F7v\x0FCT7V\u00C4oY^\x0E\u00D1w\u00A3\u00F1N\u0081Iy6\u0091\x12\u00C4\u00A4\u00E6\x1A\u00F6\u00D4\u00B7\u00BCW\u00CE\u00F7\u00C4\u0086\x10\u00DE\x0E\u0081j\u00DDQJP\x19\u0081\u00D0u\u00CC\u00F9\u00E2\u0098{QM\u00B49\n\x00&5\x19BUi\u00C1\u0084\u00AA%\t\u00DA1W\u00C5\u00F2\u008A\x0F>\x05\u0087`\b;T\u0086\u00EBu\u00CF\u0089\x1F\u0097\u008F>\u00A1\u00B7#\u00AA\u00CDx'6\u00BE\u00F8\x1606e8\x00pwf\u00FA\u00FE\u008B\u00B4\x04w0\x02\u00A6j\u0086\u00A1\u008D\u00BD*\u00D8'\x03\x0E\u00C8\u0080M\x01\u00B6\u00A0\x18;0v\u00C1B\u00ADL\u00BB\x11\u00B7\x0B\x01\x11\u00DF!\u00DA\u00D2\u00A8\u00BB\u00C34F3\u009A\u00CF\u00BF\u00B9\u00CAQ\u00BE\u00D3.\u00A04P\u00DA\u00A6.\u00DD\u00EB p\x03\u0099t5f\u00F6\x1F\u00F7\u0098P(zx\x1D\u00CA_0:>\u00A63&\u00F1\x04*i\u00E6\x16\u0094f\x06\u00B5\u00C6\u00D0*GU~N\x17Af;\u00BA\u00B7#j\txt%\u00B8\u00AFd\u00E6\u00F8\u0087\u009A#T\t\u00CAq\u00AA.m\u0080xv\u00A41\u00F4\u0096\u00EF\u00D1\u00C7FH\x0E(\x10\u00AACaR\u00C1\u00D0\u008A\u00AA ^\u0086\u00DD\u00E1\x15\u008D\u00C1\u00B6#t\u0081\u00CD\u00C5\u00B5Ji\u00A9\u0091.\u00EC}\u0093=\u00F6Da\x02\u0098\u00C14\u00F4~7\u00D6\u00F9|\u00D3\x13\x05\u0087~\u00A9\u00A6\u00B1\u00BE\u0082\u00A2\x06k\u00E5\u00C6\u00ADpDAAW\x18\u009B?\u008B\r\u00B3\u00AC\u00E5b\x06I\u00A3\u00ADf2Y\u00D7\x12z\u00CE\u00E3\u00E2\u00D1\u0093\x151#\u00F9g\u00ED\u00F3\u00BA\u00CF?A\u00BF\u00F0\u00F1\x13\u00B1\u00D2v \u0084\u00CB\u00F5\u00D3O\u00FA\x00\u00DE\u00BB~\u00CC\u00D1\u0081\u00EBsf\f\x02rQ &\u00B1\u00EA\u0082)u\u00EAb\u00B3\u00F3\x18SpP\x047\u00A9\x15g\u009D\t0\x13\u00AB\u008C\u00C4x\x01\u00C0\u00F6\u008B\x14\x01\x18\u00A3\u00D8`\u00F6\x03\u0086\u00D6r\u00A9\u00D9\u00F6b\u0099\u00B3\u0090\u00A8@\x10\u008BQh=f\u00F2\u008D\u00A2a\u00C0\u00A8\u008C\u00844\u00CE?\u0082\u00BA\u00B7\x19\x11\u00AD\u00D2\u0092\u00E5G0\u00C6\u00813\u00C0\bE\u00A2\u0084L\x00\u00F2\u0081\u00C3\t&\u0082\u0099\u0098\x1D\u008A \b\x03\x0E\x1E;Q|\u00F21cQ\u00FD\x18\u00C8\u0083\u009Ak\u0098\u0096f\u00F1\u00DF\u0095\u00F7\u00F6]\u00A2\u00E0\u0086<\u00BE\u00AB\u00A8oP\u00DF[\x05\u00DBD\x0F<w,\u00E7M\u00C3\u00B8\x12\x1DQ\u00ADa_c>{\x18\u00F7xOw/JJ\u00CB\u0098\u009B\u008A\u00C9\u00B5\u008E\u008B\u0086\u00C7\u00D2\u008F\u00C3\u00EF\u00A9\u0085\u00BF0\"~\u00A6D\u00AFT\u00C8\u0089\u00F7\u00ECE\x06\u00A9\u00D9\x14\u00B6\u008C\u00AA\u00D4\u00B2\u00D9M \u00AE\x1CN\u00AE\u00EC\u0082|\u0094/\u00B5E\x1F\u00C1l&\u00C75\u009EE\u00AF\u00FE\u00C4\u00FA\u00D6\u00DB\u00DE\u00F8$\x00o@i;\x18\u00C2\u0085\u009Bo\u00FD\u00FE\u009B7n\u00C2\u00A0\r\u00E3]\u00B4g\u00E3\u00ED6ui\u00E00\u00DE\x16\t\u00E3\x10l\"\n\bBr|\x0F\u008D]\u00F9\r>\u00F9#\x04\u00FBD\u00B0\x07\x16\u00CE<\u0087{\x1F\x14\u00BEAl\x1A\x1A\u00B7\x0B\u0083\u00E8jD\u00CA\u00A4\u00A7\u00E0\u00C8(\u00DE\rg\x02\u0099\u00F3`)\u00CE,\u009C\u00D2\u00C5\x7F\u00F7B-\u00AA-<\x14D<\u00BC\u008E\u00EEM\x16de<\x03\u00C3\u009E\u00E1\u00E7X \x14\u00A1v\u00DA/\u008Cp\u00D3hF\u008B8\x19YX\u009D\u00A1\u00E9%\u00D1:gH\u00CB}e\b\x10\u00C0c\u00E7\u0095:.\u0095i0\u00A8\u00AA\x07dV?\x1Fp/M,b }3F\x1A\u0091\u00BC\u00B4\u00CA\x00\u00F89\x18\u0097\x07(\u0085NNX]\u00C0n\u00C0e\x12%R\u0090\u0094J\u00CEq@K\u00B2UxH\u00FCZ\u00E3\x00\u00E5\u0092U\u00C3\b\u009B\u00CF;\u0098R\u00BDb\x7F\u0098\u00CC\u00AF3\u00F3`\x18\x1C+\u0092\u0080s\u00C0E\u00C2\u00F37\u00CEe\u00B9\u008FGv\n4\u00DFq\\\u00A7\u00F9\u00DD\u00EF\u00B11.\u00B0\u00EF\u00E2\u00F7}\x1Fn\u008F!\\\u00D5>\u00FA\u00BD\u0090\x05\u00D2\u00A2y76Up\u00E7\u009B\u00A6^\fClkv\u0080\u00D0\u0089\u00E8\u00A10\x035\b\u009B\u00CC\u0080\x05V\x1D1\u00805\r\u00CC\u0092\u00FDE\u00ED\u0098K\u00C3\x1C\u00DD\\\u0090hh\x1Eq8hn\u009BjD\u00ED\u0089NJw\u00A3\u0085/\x13\x15\u00C4\x06\u00AA\u0090\u00D8\u00A2\u009ED\u00DB0x\u00A4b\u00B8\u0088\u00C1\u00ADAX\u00B9\u00B9C#\u00E6\u0080\u009E\x06\u00CA\u00B14\x13\u0099\x04\u00B1\u00BB\rf`t&\u00C0 *\u0085\u00D7=\bfe\u0086\u00BD\u00E6?q\u0091\u008B\u0087,\u00B3\u009Aql\x0B\x07\u009D\u00D2\u00E0\u00AA\x0F^\u00C4\u00E2\x14\\\u008F\u008D:\x01\x00\u0098\x04\u00D5\u0088^\u00D0\x1C\u00D2\u00F2\t`\u00EF\u008D\x0BS\u0080\u0088\u00E0\x10\u00C0\u00AA\x1F\u00F9Vq$l\u00D2\u0082.!\u009E\u00A4\u00C5\u00ED\u00CB\u00C8PRS\u00AF\u0084\u00E6\u00918\u00EAv'I\x17]\x18J\x0B\u0086\u00E6\u009E\x1BtcN\x10\u0087x\x1A\u00B6\u00F9\u0088\u00D3\u00F6@q=r\r\u0092y\u00F0\u00F9\u0097\u00F6\u00AC4\u00A0\u00F6\u00CA\u0084$\u009F/~\u00CE=\x1E\x04j\u00A5\u00F1T2i\u00CD\x14I\x0B\u00C4\u00EA\u00D6\u00AF\u00F6\x16\x15\u00AF\u00C3\u00FE`\u00D5t\u008A7Y\u00C0\x04,u\u0084\u00D7F\u00F1\u00F7\u00DC\u00D1\u00AE\u00BB\u00E6\u009F`\u00A5M\x18\u00C2g\u00F5\x0B\x0F\u00FB\u00C1\u00CD\u00FF\u00FD\u008Cx\u0089\u00C7\u00A4\u0091\u00E0\u0087 b\u00E3\u0084\u0083?(\u00D3\u0080\x0BD\x02\u00BF\u00A7\u0084\x03\x10\u00D2;\u00FD\u00F469[\"\u00D8\u0083\u008E\r\f^\u0098\x04`Y\u00B1&\u00D5^\u00A1h\u00A2Pt_\u0087\\\u0084\u00B5w.\"c^)\u008F\x19\u00FC\u00E4\x05Q\u00A4\u0085\u00DCqYm\u00D7\u00BA\u00EA0\u0082\u0092\u0094s/\u0091\u0084\u00D5\u00D1|\u00D3\u00D9L\u00D1\u0099\u00F8\u00C1\u00E1\u00EAIC\u00EA\u00BC\x13)\u0083$T\u00F7\x00d`\u0091f)7\u00E3\x14\u00B6\u00A8\u009C<\u00C4\x17\x12{\u00CB\u00D8d1\u00EFB\u00CF\u00F9\u00A8\u00FF\n\u00D3\u0082I@~M\u00C6\fp\u00F85B\x11\u0087\x18;+W\u00F7\u00A2\x1E\u00D5V%&\u00FB\t|\x12\u00F3c\u00EF\u009E^\u0086:^)\u00BF\u00D5.I\u0094TCh\u00FC#\x04/\u008C+\u00F6e\u00A8\f\x02\x05Y\x1C\u008A\u009A$\u00EEa\u00CC\u0093\f\u00B3\x04Eiy\x1E\u00AAAPLj8h\u00C3\u00B4\u00FC\u00DA\u008A\u00A1\x12H\u00E4S\u008C\u00CC\u00ADw\u00CC>\u00FD\u00E93\u00F4\u00A3\x1Fx\u0098\u009C\u00F9\u00C8k8\u00AA\tC\u00B8b\u00F9\u0089'^9\u00BB\u00FC\x182\u0083\x06\u00F3,x\u00C9\u00B0\t\u00C1\u00D7i\u009DB\u00AF\u00E9wJ\u00C8zM\x1Ecb\u00D2>4\u00DF\u00F8m\u00865\u00D5\u00D8\x12n\x11\u008ED\u00F3,4'\u00CA\u009A|D\u0092\u00A4'\u00A11\u00E6\x00@W#~2\x06\x120\u0097iw\t9e`F\u00ACV\u00C9\u0099\u00B5\u008F2Yetd\u0090\u0091\u0092\u00E6a3?x\u00E9\u008B\u00A1\u00D5A\u00C3+\u00C6\u00A50\u008A\x11-\x18\u00B3\u00A3\u00D1\u00AA7w\x1DR\u00DA\u00C75(\x0B{*a\x11Q\u0093\\t+\u00EF\u00D3\u00BB\bD\u00A2\u00F5\u0098\u0096\u0085[\u00FB>\x04Q\u0093(+\u0093\x03\x10\u00B5\x0B\u00AB\x00\u00D6\x12\u00EB\u00CF\u0083\u00F4(D\u00DFTE\x10\u00F9\x1Fe\u00A2\u00CA\u00B9\u00E9\u00DAS\u00E6!T\u00E3e\x04\x12\u00F1>u-\u00E6\u00D4\u00C9\u00EA\u00B8\u00E3\x1C\u00FD\u00FF\u0089{\u00B7^\u00DB\u00B2\u00E3<\u00EC\u00AB1\u00E7\u00DE\u00E7\u009C\u00BE\u00F2*6E\u009ATD5E\u00C5V\b\u00EB\x02:1\x1D\u00CB@\"8\u0092\x03%A\x12+\x01b\x04A\x00\u00FF\u0080<\u00E5=\u0081\x1F\x14 \u0080\u00DFb\u00C0\x0E\u0090\u00F8\u00C1\u0090^L#\x12\x02\u00CB\u0092%@\u0092\x05\u00CA\u00B2@\u008B&e\u00D1\u00A1\u00A9\u00B6D5\u00D9d\u00F3\u00D6\u0097s\u00CE^sT\x1E\u00EA\u00FB\u00AAj\u00CC\u00BD\u009Bl\u00C1\u00BA\u00AC\u00EEu\u00D6\u00DAs\u00CD9\u00E6\x18c\u008E\u00AA\u00FA\u00EA:\x1AZ\u00A8u\u00D3\u00EF\u00EF\u00B29\u0088\u00F0\u00DB\u00DC:=\fr\u00B3\u00F6\u00A2\u00AB*>\u0083d\x18\u00FD\u00F6\u0086\u00ED\u00E5/\u00E0k?\u00F3\u0093?\x04\u00E0\u00FF\u00D2\u00E1\u0085!\u00FC\u00C2\u00CD\u00CF\u00FF\u00C5\u009B\u00FB\u00AF\u00E1O\u00EC\u0095\u00FE\u00F3^\u00B8\u009C\u00E4\u00A4\x00\u008E\x1CT,\u00A4\u00DA\u00E0\x13-\u00D0\u00A5$\u008E\u00A9-\u00B7<\u00D7\x11\u00EE\u00C3\u00AF\u00BB\u00E3I\u00F3,\u009A\u00B2a\u00C3\x05\u00B1\u00ED\u00D7cJ\u00DD*\u00B0*\u00A3\u00DF`\u00AB\u0083\u00E9\u00CA\f\x7F\u0096\u00A1P\u00D26U\u0080-\u009FwH\u00FA\u008A\\<\u00F2\u00CC\u00AA\u00B1X\t\u00B2\u0081\u0084\u00C2\u00F0\x17\u00C1Q\u00A5npd\x06\u00A4;\x0B\u0096\u00B6\u0084\u00A0{/\u00A9\u00E5c\u00A5#\u00931-\u00DA\u00AE\u009CF\u00E9\u00C1*)\u00D6\u009E\r\x17\u00AE\u00A0'\u00CCs[2\u009E\u0080Jr\u00C2\u0089\x18\u00A5\x1C\x11\u00F6fX\u00F7\u00A0JO\x1B\x01\u008B78\u009D\u00BBz\u00CCI\u00C0mw\u00EB5:\u00D0\u00D9n\u00D93\x14\u00F7Q\u008C@s\u00A5\x10f\u0084\u00E0\x11\u00F7t\u00A1+\x05\x18X\u008D\t\u009D\u00BDw\u009B\x06\x0B\u009F\x0E\x00\x07%\u00F5l\f\x03\u00D1'\u00F3\x1E\x14\u00C5y\u00F4\u00AE\u00947\u00CE1\u00F9\u00BC\u00BCv\u00D3\u008A{7\x17hhX\u00C9\x002\u00D5\"\x19\u00A3\x10\u009E\u00AF\u00E8!\u00D5\n\x07&\u00B0\u00CF\u0089\u00FD\u0093\u009F\u00FE\u008Bx#\u0086\u00F09{\u00E9\u00A3\x07zG\u00FF\u00B8_\x03\u00EB\u00AE\u00BDUG \t;\u00A5\x7FI\u00D6\u00EE\u00AE\u00A97\u0090aD^@_\u00D7\u008Eix4\u0080\u0097\u00DC\u00C3\u0086\x000\u00AE\x00\u00D8\u00DC#\x12\x11*\u0093\x16\u00F1\u00F9\x13\x1E\u00AED\u00AB\u00D2i\x07\u00E2\u00FA\u0083\u00EBj\u0098\x12\u009Dr[SXJwp\u00A9g\u0089\x16DmGY\x01\u00C2 T\x1B\u00CEV\u0088\u00B3l\x16\u00950\u00C5\u00D2ln9\u00CE\u00B4\u00FEC\u008CA\u0088i,\u008B\x05\u00C9H\x10-\u00E5z\u008C\\\b\x11I\u00AE\u0083D\x07]\x14#\u0088\u00DA.1\u00BF\u008C\x7F\u00D0B\x0F\u00E2G#\u00AC\u00B6\u00AAD\u00C8\x16*A\x1C;?\u00D7\u008E(\u00C4\u00CDJ\u00FDK\u00F5&\u00D5\u0091\x16\u00A4\u00E3\u0086\u00C5\u00D2\u00AE\u00B5\u00D5\u0099\x14\u00DB\u008C\x023rm\u0093\u00D0\u009B\x07\u00A1\x03\u008F\u00EA\x7F\x19\x1Cm1\x1E\nI\u00E8J#\u0081w&\u00D1Q\u0087\u00B5{\u00F5;\u00E9\u009C\u0086j\\;II\u00ED\u00A9\u00B0\x00\u00AD\u0087\u00A5\u008DTi\u00BA\u00D7\u00C2k\u00F8\u00D3\u0082\u00A9|\u00F1\u00C5\u008F\u00F6\u00A1%&\u00FF\u00DA|\u00F5\x1D\u009F\x1B\u009Fz\u00BE\u00FC\u00F8\x7F\u00DC\u00AFB\x07\u00F1\u008E\u00A0\u00A4[*\x01\u00CF\u00AD\u00CFB\x03\u00BD\u00AD\u0080\u008D\x15\x11x~\x05\u00DA\x0EW\u00E4\u00D71\u00F0\u009A\u00EDx\u00E4\u0086G\u0088J\u00CD7\x0E\\(\u00AB.\x16\x01J\x17\u00C6*\x1C\u0088\u00B0\u00E5xo\u00B8Al/\u00DF\u0093\u009D\x0E\"\u0080x\u00EBX1\x11\u00C5\u009A\x1D\u0098\u00BC.v\u00A9\u008E}#\u0090^\x06\u00B93\x0F\u00AF\u00EBj\u00D7\u00A9\u0088-P\u00C5\u00E5x\u00CE\u008Dq\u009A\u0085\u00CD@s\u00AA8\x03#\u00A3\u00D5\u00DF\u0083\u009F\fWVT\u00E4\u00F2V[ryj\u00F1\u00DB\x0E_\u00FC\u00F1#\u00ECzz.C\u00EA\x1F\u0090\u00D9{\u00EA\x1B\x14\u00B6\u00CB6\u00B9`U\u00C9\u00CA\x19Ay\u00FB!\u00B6>\x01\u0090\x01\u00B1b6\x001\u00BA\b1n\u00EB(\u00C3}G;fi|C\u009F;\u00FD\u0086\u0090\u00E8\u00BDL}_g\u00C8$\u00ADF\u00F8\u00F2\u00F2\u00F4k\x14f\u00CC\u00FE'\u009F\u00D5gB|\u00B5\u0081BD4l\u00D6\u00AB\u00D90Z?S\x006&\x14\u00CC 6\u00DBq\u00AAO9\u009E\x17_|\u00DE\u00FF\u00D5\u00AF\u00BFC\u00AD&B\u00F8\u00B8}\u00F6\u00CF\u00FD\u00D6\u00FC\u00B4\u00C1\u00FE\u00A4\x02\u0092\u00A2|I1\x05@\u008B\u00C0\u00F3\u00BB\x1E8\x07\u00DA\u0085N\x0B\u00D81J\u00CB5\x07@\u00EF*\u0098\x1Ar\u00CC\u00F1U\x00\u00F7\u00A6c\x1B\u00B1I\u00EC\u00EE\u00AC\u00A3\u00C0H\u00C1\r!E6T\u00AC\u00C1\u00C0\u00A0\x07b\u00E3.Q(\u0082 \u00E2P\u0090\u0093\x1EK\x14M\u00A9\x07w\u0085\nb\u00BA``\u00E3\x18\u00D4~\u00EE3\t\u00ED\u00E1P5\x15\x00\u0085k)@K#\u00D7Bi\u00D2\u00B2Gmv]\u00D24\u00BB\u009C\u00B7\u00F4U\u0083\u00F7j\u00B3o\r\u00E66\u009F\u00F9j\u008B\u0088\u00A6,\u00A1=c\u00FC\x01\x06,90\u008F`>)\u00C1%\u00C1\x06\u00BB7\u00B9\u00CB\u00DA@l\x0Bg\u00C8\u009D\u009E\u00A8\u00AFgr\u00D4t\x12/J\"\u00C2q\u00CE?\u008D\u00B1\x15a\u00BA\u00EA\u00CA\x11B;\u00A1w\x15ia\u00D7Dd9g\x16v\u0084\x05o\u00A6u\u00E8\u008E\u00F8\x042@?\b\u00ED=\x04\u00D5\u00AD\u00E2\u00AB\u00CD\x0B\u00E1=Q\u008B\u00EA\u00C0\u00ECc\u00D15\u0097\u0094\u00F4\u00B9\u008B8\u0091Dz`\u00BA\u00BB\u00D1\u00DB\u00E3NDh\x18/}\u00C1\x1E\u00FD\u00FC?\u00FAs\x00\u00FE\x1F\u00A01\u0084\x7F\u00FE\u00E8\u009F\u00FC\u00E0\u00D7\u00EF\u00BDae\u00A5?\u00E2\u0097\x16eS\x17\u00806ae\u00F3\u0086o\u0094H\u008Al\u00B3\u00C6\f\u00EA\u00FC7\u00FF\u008E<\u0084/Z\x04)_aFz4{f^E\u00D6v'\x02\u00B0\u0088O\u0088\u00A5\u00E7\u0084\u00FB[\u00EC\u00F0d\u00D1\x03\u00ED\u00F3\u00AB\u00D8\u0082\u00D5\u00D5\x18\u008B\u00EF\u00E0&\u00B4\x07\u00C7\x11*\u00CBF\x15O\u0089Ib\u008A}?\u0083x\u0098!P\x14\x04\u00A3\u0099\u00ECsUR1\u00D5fI\x1E\u00A0\b\u00A9UN.\u0097{ \b\x11t\u00FC|R\x19\u00B4\u00F0\x10\u00C5>XG\x19J\u008ENv\u009E\u00CA0\u0099\u0093\x03k\x1E\u00836\u008E\x19dJ\"\u00AA\r\u0081k\u00AD5#)Z\u00AB<\u00F8X\u0093\u00BC^\u00B3\x10]\u00E0\u008C\u00E8\u0092D\b\u0092\u0096\u00D9H\u00B47\u00EB\u00DC\u0094\u00D4yO\u00F5`\u00B6\u00F6\u0089h\u00D3C\u00D0\u00D5\x1D}\u00F7;l\f\x03\u0085\nd\u00B4\u00CC\x07\u0089\x1E\u00C8\u0094\u00E7&\u0081\u00B7k\u00F5\u00AC;\u00A3\u00F6\u0081,\\\u00DBJ\u00CF\u00F5\u0097\x01\u00B8w\u00B9\u00E0\u00CB\u009F\u00FA\u00E4\x0F\u00E2\x16C\u00B8\u00F9\u009D\x1F\u00C0\u00F5\u009FP \x12\ts%f\u00A0\u00A0n{(\x0B\u00E1\u00F7\u00D78]\x7Fn\u00AB\u00D3\u00C1\u008A6\u00DC\f\u008Fp\x0F/;p\u00C5\u00E8E\u00B3H\u00BE~\u00DC\u0096x\u00B46K\u00EAzd\u00DBE\u00CEC\u00DCJ\u00E8c\u00A0\x1B\tU\u00E19z\u0093\u00DF[.\u0084\u00BA9\u00E0\x11\x19\u009981\u00FAY\u008A\\\u0090\u00DA\u00C6\u00B3{]FS%\u00EA\u00A9\u00FD\rF\x0E\u00DA\u00BC\u00E6Bkd\u00B8\u0095\u00A1\u00AE\u0095G7\u0080\u00BEk\x00C\u0090\x13\u00B0\u00D12\u00FB\u00E0\u00B0a\x19>k\x18\u0098\u00D4\u00E9\x1D\u0091\x04\u00E5\u0083\u00D2\u00CE\u0082\u00C8\u00E7\u009C\x19\u0093\u009F\u0088\u009C\x022\u00E0\u00BD\x10\u00CA\u00A8\u0085\u009FO\u00CE\u00B1\u00DA\x01,\x17\u00BD\u00B9a\u00FAAb\u0092~/\x0E\u00A2ur\u00D2\u00F5s=9\x0B\u00D7\u00D0;C\u00F5!\x18\b\r\u0082\x1A\u0082m\u00E9\u00FAt\u00868\u00CB\u00BA\u00EFS\u00F1\n\u0088\u00FB\x1E\u0081`l\"7\u00EF\x1D\u00B0@\x0B\x1CG\u00DAc'\u00DA\u00EA\u00908\u00C9\u00C5\x1A\u008F\u00E7\u00F0\x005'\x03\u00A2\u00C2\u00AC\u00DDQ\u00FBo\u00A4\u00C1\u00D5\u00D8\x1Fk\u00E7\u0098,\u00DC\tL\u009E\u00F8\u00D7\u009F\u00FD\x01\u00DD.\x19\u00C2W\u00AE_\u00F8>7e7\x16\x11\u00FD\u00D1\u00BE\b\u00A9\u00B0\x01\u00B8\u008FH6:\x13<\u00FF\u00EE\u00BA\u00B1\u008C\u008D~\u009B\u00E8MF\u00B4s\x1B\u00E7\x1C\u0080\u00BC?-\u00B5\x06\u00BC\u0086\r_\u0081\u00E1\u00BEE\u00DD\x04\u00ED\x13\x14F\u00C3\u00C8\u00D6?\u00BCX\u008D\u008C\u0086\u00B1wdH7\x19\x15o\u00C8\u00E4\x14\u0087\u00A0\u00FEt\u00AFB\u00A6\u00E8\u0082Q\u0088\u00A88\u00BDH\u0085\x0E\u00AF\u0084\u00E4pW!\u009C\u008C\u00CC]\u00E5\u00DDc\u009C\\\u00E2\bT\u00A7@\u008A\x04\x00\x00 \x00IDAT\u00C5\u00DF\u00C8\u00BC\u00CAR\x0E\u0098\u0095A-\u00E6\u0087\u0090\\sfB\x04E\u0098\u00AE2iMj\u00E7K\x014\u008E\u008Cp\\\x11E\u0087\u00F4Hi&tcy\u00E1@\u00ED\u00A9\x10\u00CF\u00D8\x16\u00C9[\u00CC\u00DEM\u0086\u00CB\x16U\u0099\u00AE\u00CF2\u00FC%\u0093I\u00DF\u00BD\u00C6eK4\u00A6\u00DD)}i\u00DA\u00F5\u0089\u00AA=\u0080ZK\x1Ak[S\u0096\x15\u0099t\x02\x11\u0080DA\u0081\u009B\u00BA\u00D72\u00CE\u00D6\u00B6\u00B71%\u00D3\f\x06\u009B\u00EB\u00CA\u00DB\u00F8rA\t\u0089\u008Cv\u00BD\x15(\u00CB>O\x1C\u00AF\u00BE\u00FE}\u00D5{\x00\u009F\u00F3\x17\u00DE\u00F5{\u00F3w\u009E\u00FB\u00E3-\u00B5\u00BE\x01\u00FE\x00\u00C3\u009F\u0086\u00F9\u00D3\x00\u00EE\u00C3\u00F1\x00\u00F0\x07\u0080\u00DF\x03\u00FC\n\u00C0\u008E\u009E?\x7F\u00CB\u00AF\x0E\u00E0\u008C\x18j\u00F1\u00B7W\u00B3\u00C4\u00AF\f\u00A4?\u00B4\u00A8B\u00FC\r\x07\u00BE\u00E2\u0086G\u00BE\u00E3\u00B1ox\u00E8\x1B\x1E\u00FB\u008E\u00C7\u00D8\u00F1\bWxl;nl\u00E4\u00FB\u0082\u0081\x1B\x03n\fx\u00CC\u00BF\u00C3\x18H\x15\x02R\x17D\u00C8\x15\u0096\u00AC2\u00ED\x077\u009F\t\u00B5\u0083oh\u00A7i\u00C5\u00B0\u00A9\u00DEc\u00B0\u00A8If\x13F\u00C60\n.IDRXZ\u00A8\u00B5\x18\u0089I\x1D\x11\u00E3L\u00D5\u008CjX\x1A\x0E\x17VKU\u00AD\u00BBV\u00DB|\x12%X\x12Z\x19\u00EEn\u00D9r\u00DA\u00F4\u00C7\u00B3Q\u0082\u0094-\u00A7\x14hQ\x1F\u00D9\u008Fdt]\u00A0\u0088)\u00CE$\x00\u00CD\u00F7*\x10\x1A\u00D1\u00E7\u00F76\x07\u00D2\u00C7\u00BB\r&\tJ\u00EB\u0085B\u00C99\x1F\u00DEB\u00E0\f\u00D5\u00EE\u00C2\u00A6\x151hK$\u00A2\x1C\u00CF+\u0083U\u00BF\u00C44\u00E2\u009Fr\u0082\u00AA\u00D5Q\u00FD\u00F0& [\u009F\u00A5\":\u0099\u0081/\u00F7\u0089\u0095\u00B9\x7F\u00E1\u00F3\u00CF\u00F9\u00EF\u00FC\u00E6\u00BB\x00\"\u0084\u0087\u00B0\u00EF\u00FD\u00ED\u00F1/\u00D1\u00CE\u00FC#zQ\"c\x0B\u00E2\u00B7k\u00A8(H\u00EE\u00E3\u0097\u00BB2\r.\u00E2^$\u00A5G\x11\x02\u0099}\u00D6\u008F\u00A1\u00AD\u009D;_Z\x18*\u00CB\u00D5$\x0E\u00C2{\u00F0\u00B2\x03O\x00\u00D8|`\u00B3\u0089G\u0092\u00B2\by\u00D1\x1F\u00B4\x02\u009D6\u008FH\u00D6\x1B(\u00847\u00D0x\u009C!5\"\u00DE\u008A?\u0088\u00AC\u00CAZ\x10[\u00BB\u008F\u00BC\x0B\x002\x19*\u00AA@\u00D7\u00EB\u00E0\x1C\x1D}n=T\x07\x10\u00F5\u00C0\u009C\u00D2b\u00A3\u00E6\u00D5\x12\u00AE\\\u00BBJ\u0099\u00A2}\u00A1-\u00E7\u00AA\u0090\u0091\u00E5\u00BC\u00C6\u009C\u008F\u0092\u00AAF\u00FD\u009B\u00D5\u0099cj\u0084H8\u00B7Q\u00C6y\u00F1\u00AFG T\u00E8\t\x11\u008E;\u0097\u00A5\u00E7Z\u00C8w2\x7FO\x02\u009Es\u00B6\u00E71\x1A\x11\x11s\u00B9\u00B2\x1CWC]Iw\u00AD)\u0086P\u00BB\u00A3\u00D4S\"\u00A4\u00CC\x00=\u00EA\u00BAY\u00D1\u0096\u00B5U\u00DE\u0089v\u00C8@\u00B4\u00A5\x1A\x1C-\u00E6\u00A3\u00ADC\u0097*@&1i\u00B8\u0094\u00BDb\u00DA\x1Dm7u@1\u00F1\u009Da-\u00A7\x13\u00E5P\u00BD\u00F0\u00D9\u00F7y\u0088\u00F3\u00B7\u0097^\u00C2+?\u00FF\x0B\u00DF\x0B\u00E0\x0B;\x00\u00FC\u00C2k\u00BF\u00F9g^\u00BB\u00FAFM\u00F8\x1F\u00EAK\u00D0kC\u0096Q\u00C7\x15\f\x0F\u00E0\u00B8\u00E2X\u00E3\u00D8`\x1A\u00EE\\\u00D4\u0081\u00BD]_AG\u00EE\u0092b\u00FD\u00DD\f\u0092}\u00E2\u00AD,\x00+B\u00E8RC\u00B6\u008A\u0081G\x06|\x15\x07\u00AE1\u00B01\x1FrO\u00B7\u00DFl[\u00C7\x07A\x1D\u00EE\u00B8 \x16\u00C9D\u00B8\b\x07\u0099\u008B\u0081y\x10$p[\\z\u00C0\x15\u009Ca\u00CBA\x02e#\x0E\u0089\u00A2\u00FA\u008AR)\x0E\u0084\u00F4\u009D)\u00917\x14\x18\u008Ex\u00C6\u0090\u00D6X$_n\x06K\u00A3\u009DBr\u00E5\u00E2\u00CB\u00B8\x00o\u00D3\u00E3[-\u0089\u00E4\b\u00FA\u0083\u00C4\u00BE$\u00FE\x14Z\u00A86\u009B\x1D#\u00F5x\x05\u00F6\u00B4\u0086]\u00D9\u008AvZ\u00D4\u00DD\x1F_\u00CC<\bL\x12\u00D1C\u00A0\u00F8\rR\u00AC\u008A\x01e\x1B\u0093:\u00B4\x18\x07\u008D\u00A0KVb{;Pp\u00BB\u008D9a\u00BD<\x1E\u0082\u00EA\rI\u00E8\u00EC;\u00D0M \u00A2 h\u00CF\u00FC\b19\u00E54\u00A8\x1B1\u00DE<\x078uSk\u00DE\u00D7{w\u008FI\u00F7\u00AE\u00A8O\x1A\u009F\x0F`\x02W\x07\u00F0\u00F0\u00E3\u00BF\u00F2g\x00\u00FC\u00A3\x1D\x00>5~\u00FD{\x06.\u0098\u00EE\u00DFB\u00BA\u00FEA_\u00E4N\u00D8`\u00B8_\u00D2\u00DE\u00AF\u00E1\x16;8'\u00B13\u00AB1\u00BD\fK\u00C9,\x11\u00AC~\u00EB\u0088Ae\u00BCF\u00BB^\x13\u00C2\u00AA:X\u00890^\u00CA\u00A3\u00EF\u00A8\u0083\x10\u00D3\u0081\u0097\u00CD\u00B1Q\u00F2\u00EDn\u00B8\u00C2\u0086*\u008C\u00A6jM\u00D1\u00CEN\u00C6\u00A0`\u00A4\u00D8N>\u00DA\u00DB\u0081T\x19\u00CA\u008E#\u00DB\u00C1m;B &1\x1B\u00C1W\x16\x7Fu\x05>\x11q\u00E88\u00F5\u00DCn\u00AF\ba\u00DD\x18\x1D\u00CAh\u00B6\x14%\x05\u00FD\x01\x14\u00DA\x19\u00C8\x04TT!\u0098\u00D3\u0091\u00AA\u00D8\u00D0\u00CA\u00AA\u009C\u0085|\u00E4\u00AA\u009B03h\u00C7\u00A4#s\u0091\x062St!8\u00FA\u00C1\u00C9\t\u00A3[FD\u0082\u00AAJc<q\u0098s\u00ACtb\x0F\u00C7\u00E7H~!&2\u00A0\nE\u00F1\u00CC\u00E86\x14\u00A3S\u00F0\u0096\u00A3\x11'\u00FB4y\u00B7i\u00E5y\u00A0\u009B/\u00CFI\u0084y4\u00A9my?\u00C8V3k\u0085\u0095tv\u0094\u00FB\u00B5\u00FA\u00DB\u0099Vy\t\u0082\u0089\u009Av\x7FR\u00B1\x14M\u0088Y\u00B5\u00D3\f\u0089\x1D5\u00C4\u00DE\r\u00C8u\u00A0\u00DF\x07\x1C\u00E3\u008B_\u00F9\x1E\u0080*\u00C3\u0097\u00F1\u00E5\x0F]\u00B6\u00CB\x1F23\x00\u0082 \u00AFQ\u00AA\u00C0\x15\u009C\u00C7\u00ACm\u00D8\u00AA\x1D\u009Dg\x12{ \x03k\f!\u00D4\x03]\u00A3\u0087\u00A0\u009A\u008A'f\u0092\u0088\u00E2\u00AE\x01u4q\u00F7K\x15\u0099\u00BF\x06\u00E0\t8\u00AEp\u00C1\x0E\u0099\x03\u00D5g\u00B5\u0094\u00C1\u00CBI\u00D8\n\x1C5\u008E\u00D6\u00CDi\u00BD\u00B5\u00BCF\x06\u00C6s\u00BF\u00B2\u00EC\u008BEf%\u00AC\u00DB\x01\x1C=>M{Q&\u00E3t\u00F5R\u00B0\\h\u00AA\u00A4vx\x19K\x1B\u00F5d\u009A\x0E\u008C\x16\u00E6Jf\x01x\u00C6\x00,`\x00\u0092\u0080\u00CDXg\n\u00B7\x15w\u00D1\u0089d \u00B9\u0093\u00B2\u00AEQk\u00A6\u0089\u008Fs'PQ\u0083\u008E\x15%\u009C\u008F\u00E9\u00A5\u00E8E\x1A6\u00C9\b|\n\x05\u00A0\u00FA\u00C9\u00A1\u0099\u00DCz\u0082\u00E5\u00A9\u00877\x15&\u00F7\u0087\u00B0UJ\u00AB\u00DF\x1A\u00EFb\u00DD?\u008D\u00EB\u0096T\x17\u00DAh\b\u0087\u00E7\u00D6\u009E\u009E\u009Dq\u00A0\u0098L\u00A2'\u00DE\u00A3\u00AB\fh\u00E7\u00F4\bI\u00AA\x16b\x06\x19\u00E3\x00\u00CF8\u008C\u00F9\u00CA7>\x04\u0090!\u00BC\u008C\u00AF<\u00EF\u00D6&\u00EB\u00DF\u00FA\x15\x04\x19j\u00C1u,:\x17s\u00D8`\u00B8\u0086\u00F2\u00FC\u00CB^\u00D0\u00A5\u00FE\u009A?Pp\u00BF3\x04\x19\u0092J\u0095X\u00CA\u00AC-\x05>\u00CF\u00AFv\u00DE\u009D}\u008FE\u00F7\b\u0086/9\x03\u008F-\u00AE\u0089\u00AB&\u0086E]\u00C6\x0B<\u00F7\u0082\x149\x1B\x03i\x06\u00A2&\u00C2`\x1E\u0086B\u009F\x0F[\u00FB\u00A2\u00B1.\u00EEI\u00E2;\u0095\u0089\u00A9\u00A8\u0086b'\x15\x1E\u00CD\x14%sd\u00BC\x03U\u008A\x10\u00FEc\x11&\x159X\u008C\x06\u00D2\u009F\u00D1\u00C8\u00D4\u00FB\n,\u00DB\x03l6\u00B4P\u00BE\u00EE\u00CC\u00C30Gn\u00C0\"\u00D6(co\u00BA\x16\u00CFs?0\u00CD\u00D3`Y\u0084?\x1A!PZ\u00BA\x18\u00C2\u00FA\u00DC*\u00F6b\r\u00F4)\u0082U\u009B\t# F\u00AC\x04\u00A2\u00BAg\u00F5\u00BD\u00A4m\u00BB\u00DD\"\u0081\u00C5\u00FA\u00F5\u00F4\u00CE\f(\u00DA\x121.F\u00CB\u00F4+\u0089\u00C1\u0096\u00A8)\x06\u00D2\u00EF\u00D9\u00A6\u00D5\u0085\u008386\u00A7\u00AD\x06\u008D\u00A9\t\x1D\u00F8\u00CA\x104<\u00B3\u00E0\u00A1W\u00AF\u00BC\u00F6<\x00\u00EC\u0097\u00E3\u00F1\u0093\x7F\u00FE\u00F2\u00A3\u00CF\u00E1\u00DF\u00FA%\"e\u00E9\u00F4D\x05\u00C1\x04`\x1B\u00DC\u00AFQq\u00FD\x0600\u00C7\u0093\t\u0088\u00E8\u008BQ\u00DC\u0096\u00FA!\u00A5\u00B5o\u00E2\\\u00EE]\b#c\u00DEq\u00F60lm\u0092\u00AD\u00DE\u00A7\u00C2*bB\u00AF\u00C2\u00F1\x15l\u00DC@\u00D6\u00B2^\u00C2\x15\f\x17\x1F\x186pL\u0095/\u0093V\u00CF\u00BD\x1C`\u00B8\u00B4 \u00AA\u00D1\u00A4\u00FAX\u00D4\u0085JZR\u00C4\u00E3A\u00DB\u0083\u009B\u00EA5\u00D6\u00F9\u00CA\u00A84DN\u00C5A\x06\u00A0\u00EF\u009B\u00957!^#-\u00E1\u00AB\u00FB5\x16\u00C6\u0098\u0083,%\u00DC\u00B9\u0086Iz\u0093\u00B4s\u00C0\u009Ar\u00E3\x05}'\u00801\x1C8\x18\u00DB\u00D8k d\u00CC\u00FF\u0084\u00CD\x19\u00CAB/\u00BCB\u00C9\u00EDd2c\u00C6\u00B9KYu\u00DB\u00A1\u00A8<\u00CB\b\u00BF#\bx\x0E\x1A\u00C9\u00C8 &\u00EF)~\u00C1\u00C8\u00C0,\u00B2\u00EA\u00CC\x15q\u0083Mn\u00E1\x0Ea6\u008B\u00F32\u00D0\u00D0\u009A\u00DF\x7F\u00D6\u00D85\u00A7\u008B\x04\x17\u0083\u00E3\u00BD\u00D2\u0088\u0089\u00AC\x1F\u00A1 #\u00D5V\u0080\u00EA\x19\u00F0o;\u00C8\\foS\u00E8,\u00C6#\u00E3\u00A4\"+\u00E5\u00EAM\x16\u0097\u00A8Gv\x1B\u00CF6\u00CD\u00A5\u0092]\u008Aq\u00CC\u0098{\x1B\u008E\u00F1\u00F5\u00AF>\u00E7_\u00FC\u00EC\u0093\u00FB\u00A7\u00C7\u00A3\u00EF\u00F8\u00A2\x7F\u00FE\x0F\x01\x1A\u0088\u00F8w \u008A\u0094\u00C1\u00A9&\u00C8m\u00A8\u00B0\u00DEr\x05m(/B#j\x18b\u00CB\u00F7b\x06Ys\x00\u00C5@\u00EA:\x1A\u00A3\u00D2\u00EE\u0080\u00DB\u00BF/oq`\u00E9\u00D7\u00FD\u00BC\u00D5\x009\u00CD\u00F05\x18\x1E\u00D0\u008E\u00B0\u00DBZ\u00A9\u00C1a\u00F4&\u00C4\u00FB\u00B2\u00DCE\u009E\u0085 \u00F2KJ!\u00D9\x1F\u00C4\u0088\u008AY\x053(r\u0096\x17#\u00A2t\x15\u00B6\x1Cm\u008B\x11\u0084\u00CD!\u00C6=\u00DB}\x13\x05\u0084\u00D9\u00BEIP=\u00EE8\u00ABB\u00AE:\u00FE\x00\u00C4\bJ\u00B4\u0080\x0B^\u0092-\u00B6\u009F)\u0089\u00DD\u00C5\u0097\u00B5\b\u00C2\u00F0\u00B4D\u00A0\u00A2\x16\u00BB\u00AE\u00EBbW\u008B\u00BC!\u0080\u00F4\u00B1c\u00E9w1\x1E\u009D\u00DFavk/\tx\"\u00ED\x05]2w\u00C2\u00D6Ks\u0096(b\u00A0vE\u00EA\u00C7\x1A\u008A\u00C8\u00FEKZ{^n\u00D9\u00DE\x1D\u00AF\u00C5`\u00D8\u00D1\u0088\u00A4|\u00FD\u00AD\u00FD\u00BA\r\u00ED\x19L\u00F6M]!#\x17\u00D3H\u008FMGG\u0099\u00F8D\x0F\u00C8\x04n\u00BE\u00F0\u0092\u00DD\u00FF\u00C4\u00A7\u00BFc\u00FF<>\u00FB\u00FE/\u00F9\x17\u00EF\u00EE\u00EC\u00B7|IrQ\x1D\u00F0\x07\u008D\u00C8O6\x02/\u0089\x1F:q7\b6\u00EF\u0081\b$\u0089[\u0099\u0081\"\u00D6\x1E\u00DELF\u0090\x0F\u00BE\x18B\u00A1\u0082~\u00AEBxuNg\x06\u009C\u00F8\u008C\x1D\u00AD\u00C5t\u00C0\u00F1\x12.\u0098\u0088\u008A\u00CCW\x1E[\u00C2)\x1D9\u008Dv\u0090\u00F6^\u00FDm\u00A4\u0085^\x19\u00C9@wb\x12\u00A1X\u0091\u00DA\x03\u00C3\u00A6\u0091\u00C7\f(\u0086\u00E0#\u00C3\u009E\x15\u00E2\u00EC(\u0086\x03\x18v\u00EF\x006\u0098\u008Fc\u00CBEb\u00CAl\u00D4\u00FAu\u00D4\x02\x1A\u0088]\u0093\u0086\u0091/H\u00E4\u00B5\u00CAG1\u008A6\x7F\u00BC\x0F\u00A5tm\u00D3.\x04\"f\u00AF2\u00B6@\x11~\u00BF^\u00F3\x0F\u00DA\x00R\x06V\u00FB\u00BA\u00D6\x00WPOJ\u00EC\u00B6D\r\u00D0\u00EEN\u00B9;\u00B6\u00B7D\u00A6\u0084\u00DE\u00ED>\u00CD\x1EP\x04\u00AD\u00A0\x1E\x06f\u00BB\u0096JS\u008D\x1C\u00C9^\x01@\tDRE\\\x1E\u0086[/^\u0091\u00FDg\x1Fd\u00D4\u00F4B\u00BC\u00F5;r\f\u00C6s+\u00C7\x03\u00AB\u0087#\x19R\u00C4\u00B1\x18Z\u00B1\x19\u00A2\u008E\u00ABG7x\u00F5W\x7F\u00ED\u00FD\u00FBg/\u00BF\u00FD\u00BE\u0087\u00FBk\u00B9\b\u00DF\u00D4\u00CB\u00B50\u00AE\x11U\u00826D \u00D1\x15\u00E0\u00D7X\u00F5w\x12~\x0B\"\u00A9\u00C1\u00B5\x1D\u009C\u0093\x19\u00A8\\Z@\u00ED\u00DA\x01I\u0093/\u00D5\"\u00CE\u00ED{2\u0096\u0085\u00BDK\u00DD\u00CEpl\u00F1,\u00B8\x01*d\u00EAP\u00AAu\u009A\u00F5\u00EAA!\n\u00A7|\u00CD\x1DO\u00C0\u00B1g\u0089\u00B6\u00E8\u00C5\u00EE\x11\u00BA\u00AC\u00F4\u00E9\b\x16r\x1C\u00A0\u00C7\u00C0T\u00CE\u00BDBe&\u00FFR:u\u0094/pL\u00D7\u00B6\u00B1u\u00B6\u00DESnH\x10\t\u00CC\u00B0mD\u00FE\x04\u00F3\b,\u0097'\u00F9\u009B!\u00C2\u008Fe)7\u00AA7q\u0082\x10\u00861\u00F0 G\u00ED@n7\u009F-\u00D2\u00C2\u00DD\u00D2\u00A9\u00C52k\u00FD\u0094\u00F45\u00B7Zx\u0090\x7F}\u00A2j\":\r\u00E8R#\u0088\u00D1\x13:\u00D3\u00BB`\x07b\u00EF\b\x0F\u0089\u0096(\u00B0\u00A0yA\u00E4r\u00DB%\u00E9Ir6\u00D8\x1D\u00C7\u00E2|m\u00C9\u00EE\u00C3aG\u00EB\x03\u00DD\u008AF\u0088mB\nn\x11\u00ADi\u00CE*\u00F1TA&\u00A0\x023\u00C6\u00EF\u0091\u00BB0\u00819\u00A9Ry\u00FE\x1DoKFU\u00B1\x131\u00E42$\u00F2\u00F9%\u00C3\u0098\u00A7\u00B1\u00AB_\u0089;\u00D7E\u00C0\u0083>g\u00C4\u00C8\u00A4Z\u00E59?\u00F7\u00E6\r^\u00FE\u00FC\u00E7\u00DE\u00B7?|\u00FC\u00D2{\u00E6\u00BD?\u00C8V\u00EF\u0082\u00D9\u00D7\x00\x1E Kx\u00F3m\u00A6\u009D\u009Ae\u00DD\u00BE\x02\x16\u0098\u00DF\u00DC\u00840\u0094j\u00D0Q\x03\u00DA\u00B9=B\u00B1\u009F\u00D7\u00A2\u00D0\u00F2S\u008B\u00E5\u00BC\u00CB\u0093\u00D4\rk\u00F7\u00B76\u0096\u00DEn\x1B\u00A7\x10\u008AK\u00C2\u0084A\u00F0u\u00D3]Fco\u00AB\u0084\x16\u0083\x1A\u0088b\u00AE\x0E\u00A4\u00FD\u00E1b%\u00F5uMh\u0083\x03\u00C3\x02\x11\u00A9\u00A2s~w\u00E6G4\u00B5\u00C3\u00C8\f\u00A2\u00D2\x12U2\u00DA\x1D\u00E4\u00BA4\u009B\u0090)tAD\t\u009D\u00C1\u00C5\x16+\u00A8\u008B\u0085UD4h\u00DC\u0092\u00A9j\u00DEY\x7F0\x11B${\u00AF\u00D7\u0091\u009F\u00F4\\\u0085^\fU\x0B\u00B5\u00FB\u00FE;\u00B17Cb\u00EA\u00D0\u00DD\u00AA/i\u0098]n\\!\u00F5\u00F8Q\u00B6\x05\u009F\u00CD\u00D0\u00D7\u00AF\u00EF#W\u00FF\x1B\u008CR{b~D\n\u00A6\u00A4\"U\u008D\u00EE\x1E\u0083\u00DE\u00C6R#\u00A1\u00882\u00E7\u00B3\u00CF\x031G\u00CD\u0099~\x17s\u00E1\u00FD\u00D2\u00D5\u00D8\u00E6LjKc\u0088\u0098-Xj2/\u0085v\u008EG/\u00FD\u00FE{\u00F6O\u00F9+\u00EF\u009Eo:\u00E5yC0\x01\x1A\rMq\x04\n8\"3p\u00C1\u00FA\r\u00A6\u0092h9!\u00A3P\u00C1\u00ADHD\u009D\x13\u00F0vv\u0086\u0090\u008Bp\u00AC\u00ED5\u0082\u00B7\u00EC\u00E3\u008E\u0082\u009D\u00B4\x00\u00F7\u00FC\u0086\u0096\x058\u009B11\u00F5{\u0097\u00F7b\u00B57h\u0087\u0081\u00CD\u0081a\u00DCW\u0081B\u00E2\u0086\u0090~\u00A3WasK\u009D\u00D9\x11\u00A6\u00D6\r\u00ABGA\u00F7\x1CP\r\u0082\x0E\u00FD\u00F5\u00AB\u00E5\\\u00E9\u00E5\u00CE]\u00A2\u00BA\u00BB\n\u008EK\u009Eb\u0080\u0089\u00DCc\x1E\x07\u008C\x05\u0089\u009A\u009D\u00C5-\u0093\x1D\u00E5%Lz\u0092\u0094\x06\x00G\u00ED\u00C6\u00C4\x13K75\x04\x02\u00EBk\u00C8\u00D9\x16\u008D\u0096\u00EAS\u00E1\u008F|\u0096f\x1B\u00A15Z\u009C\u00FE\u0089\x19d\u0092\x16W6\u00F5\u00DE\u00E9\u009EO\u00BD\u0090\t\u00E1\u00F4\u00D4\u00B3t:Q\x18\u00B5\bo\u00FD\x11CQ\f\u0081%a)y)\u00F7A\u00D05\u00D4\u00C5}\u0081\u00E9\u00BC\u00F5\x1C\u00C0lR\u00DE\x03\x04X\u00CF\u009E\u00F4>\x0FA\u00C0\x15\u00F5\u00D8\u00C7_\u00F7*\u00E2/\u00F5\x05\u00ED\x1E]\u00B5(\u00A60\u00D9?2\u00C1\u00F4\u00864\u00A6\u00A6\u00A7\u00C1\u00B6\u009E\u00FA\u00CA\u00D7\u00DE\u00BD;^~\u008E\u00A7}\u008B\u00D7\x15\x02\x15\u0084\u00C10\u0088\u00EE\x1A\u00A5\u00E7Sjg\u00ECz\x10\u00A6\u00DBJ\u00EC\u00B5\u00AF_?n\u00AD\u009DA\u00C9\u00B9\u00EA\u00FEk\u00C2R\x19\x19\u00E3\u00D5\u0099\x06\u00D3\u00A3\u00CD\u00DA\x02\x13Zimb\u0090n\u00D4\u00AE\x16\u009C\u0098A\u008D)\u00C8\r,\u00DE\u00EE\u00DCR\u00BET\u0097\u0091\x12W\x11\u0086b\u00C6m\u009Bx\u00D3Vp\u00C6\rdK\u00BD\u00E831\x01\x19\u00B811\u00D3\u00AD(\u00CF\u00CC$\u00F3\u00C8\u00E4\u00A6T-\u00E2)N\u008F\u0080\u009F0:V\u0095\u00E7$\u0083\u00AC\u00D8\u00CC\x05\u00A1\x07o\r\u0096\u0082\u00D9\u0092,\u00A2*\x0B{2\x01\u0084z\u00E4\u00A6\x055P\x16q\x11P,J-8x\u0099I]\u0090\x17\bk\u00BB\x18\u00CA\u00C9\u00D3#\u00E5K\u00CC\u00A1\u00EA\x19\x02n\n\u00C1\u00F5bJ\u009D\u0099\u00F9d\u00DB^\u00DE\u0089\u00DE\u0097DH\"\u00E8\u00D9$7\u00C8\x14\x1A\u0081\nb\u0093I\u0098\u0098\u008ES\u0086\u00BB\u00E7\u00DC\x01\u00C5X\u00D2\u008A\u00D4\x19\x0F\u00E7\u00ABl\x00\u00EDY8\u00D0\x0B\u00A1\u0094\x04G1\x13\u00CECg$\x19T\x06+F!f\u00E1\u00F5N\x03\u00E7\u00D43\x15rr\u00EC\u00AF\u00FBs\u00FB\u00EF]\u00BF\u00FC\u00CE76\x1F\x18$\u00DB\"\u00A6`Gls\u00B2\u00C2\u00FF5\x16\u00A0\u00DB\x0B\u00E8BL\u00CB\u00B6\u00AE\x0BY\u00EB\u009D\u00A0}\u00A06\x10\u00ED\u0092\\\u00FC\u00BC\x1B\x13\x0B\x15tDQjDW\x11\u00C4\u0088\u00F4\u0080\u00E3>\u00B5z\u00D4\u00AF\"\u00FE\u0090\\T=\x04\x07\u00CD\u00B1\u00DB\u0086\u00E1\u008E\u00E1{\u00C9x\x0B\u00F7\u00E0P\u00A6\x1E\u00F6\b-\u0086\x12\u009AT;1<\x10]!\u009A\u00C6\u008D^|\u00D0Uit\x0F\x1A.\u00AD?\x1A\u0091\u0092\u00A2\u00B8\u0099z\u00FC\u00E2\u00D1\x7F7\u009A_\u008D\u00BA;\u00E3<\u00CC\u0094!IF\u00BA\x18\x05{\u00F0\u0090\u00DA\u0094e\x1A\u00F0\u00BE\u00A2\u00F2\u00FC&\u00C1\r%m\u00D2(\u0088|\u00E6\u00B1(e\x11\x07R\u00DA\u0081\x16\x7F\u00BD8\u0086\u00D2\u00DD5C\u0088s\x17c\u009F\u00EENt\u00E0\u00FDw\x11\x1A\u00AF\u00EF}\u00C3\u00865kR\u00C7G\x0EG\x03H\u00EF~\x0F\x06\"\u0083\u00ABk\u00D5w\u0096L\u00A3\u00A1T\u00C6<'s,5\r\u00C8\x1C\u0085\u008Cv\x14\u0083\u00CB\u00C6\u009AAP\x03\u00ED\u008CVcT\x07t\f\u00E8\u008CF\u00CC\u00CCN}-F\x11c\u00A9jN\u00C1\\\u00E6\u00CD\u00F1\u00CE}\u00C7\u00E5\u00ED\u00B1L/X_\u0086@\x03\x0F\u0090\u009B\u00A4`\x07p\x1F]\u00E7\u00D6\u00C6\u00ABE\u00C8*E.c\u00A3\x16,?\u00DB\u00E2\f\u00E9ZFG\u00B9\u00CE\u00C2\u00D8\u00D8\u0089\u00FCl/\u00E8de\u00E8\u00E1\u00C7J\x7F.\u00EB\u00FF\u00DE\u00AE\x03\u00D6\u008AI\rnz\u00DB\u00AD\u00B8\u00E5>8\t\u00E5\n\x03W\u00D8\u00B1\u00D9\u00C4\u00E6;v\u00CC\u0088K\x00\u00F7uH\u0099OF\u0097\x1E\x05\u008DA\u00C9O\u00F1\u00BA\u00A9\x1E\u0087\u00E1\f\u0091\u00FC\u00DD\u0093\u0087\u00D6 \x1D=\u00D7b\u0086\u0096\u00AADy+\u00C2\u00EB \u00B7c\u0095Z\u0093d\u009F\u00EE\x18C\u00F3\u00D7\u0088\u00D85_\u00B1\u00D0Ls\t@\u00F5\x12\u00D9@#\u00D87R5\u009B$C\u00A4\u00FDj\u00E1f\u00FD\u00C2)\u00E2'#\u00CD\u00E2\u00A7lw!\u008AQ\u00DF\u00F5\u00C4\u00D2-W\u00AB]\u00F6\u0080,4\u0092HH\u00E3\u0092pJ\b\u00B7H_\u00C5(\u00C0Uu\u00BA\u00F5\u00A7\u00EE\u009CR5U\u00A6\u00C9\x1D\u00AB\u00E4\nmFI\u00F5\u00AD\f\u00A5\u00D5W\u0099I\u00CC{_\u00BC\u008D\u00B53\x13\u00CEIG\x0BT\u009D\u0084\x0E\u00DC\u00F5]H\u00AE1\x1C?\u00BD\u00F9|=\u00D5$\u00C7\u00E5\u00F1\u00ABo\u00DF\u00E1\u008F\u00DE6M\u00C0\u00D4['\u00AE\x11\u00F9\x07\x11[\x10\u008B\u00BC\u00D2k\u009Dh`\u00A2\x16h7\u00C4I\u00C2gFb\u00CBE(\u008F\u00C0\u00EA\u0092\u00AC\x1A\x07\u00E5\u00BE\u00F3\u00A5\u00FD\u0093n/\x0EkM\u00A5\u00B0\u0086,Nj\u00C6\u00AAj\b\u0095\u00F4\u0089\u00D7\u00BD\u00E3X!\u008C\u008Dx'R\u009D\u00E2\u0092\u00D8E\u00CAhK\u0080\u00CB\u00FDW)\u00C0\u00D0o\u00F9x\u009AM\x00\u00E1a\u00F0\f(*5\u00E2B\x06(\u0095\u00C5x.,f\u00EF@\u00CDc\n\u00EAT\x1F\u00E45Q\u00B95@EZ\u00D4\u00BB\"\u0094Q\u0084\u0092\u0084\x03\u0094\x18N\u00F1X\u0084)I\u00B9\\\u00D7(5\u00D1\x03\x19\u008C\u00AF\u0086\u00CA\u0080\u00D3\u0092\u00CA\u008A\u0082hm5x[\x018\u008D\u00B8\u00FA\u00EBdsX\n:'\x1C\u00A7\u00D1o\u00F6q7\u00A9\u009C\x12\u00D6\x01g0X7\u00DA-p\x1Dd\x06\u00EC-\u00DB\x15sr\x12\u00ECZ7\x01\u0089\br]pU\u00F4\u00B1v\u00E9\u00DE\u00D1A\u00ADs!\x1EM\u00CE<\u009DKD\u00A09\u009B\u00A76\u00DB\u009E\u0090i\x7F\u0098\u0086\u00AE\u00E6\u0099\u00FB\u00DB\u00F6/\u00CEG\u00CF\u0098S2I\u00BF\u00C7= \u0093\u009168\x17\u00FF\u00C0\u0086HS\u00EE.\u00C2\"P[\u00D4\x04\u00C2\u00C0\u0085\u00D8*\"\u00D1\u0093\u00F8\u00B6\u00F5\u00BC\u00D4\u00F3\u00B1\u00A0\u0082$d/\u00E2N\u00A2\u00F5-\u00D5\u0080\u0094\u00A2\u00D9\u00BF\u00CE\b\u0080\u0085\x19\u00F4\u00E3V\u008C\u00C8\u00FBy\x00\u0080-\x1D\u008D\u00AB_\u00A0\u00F7GD\x1A\u00D7f\u00FEA{\u00F70%\u00CD\u0091\u0082\u0081\u00B6\u00A6\u00E6D\u0095e@E\u00C9\u00E2X\\3\u00C1 %\u00AE3c\u00D8\u00F4\u00C4\u00A0\u00AA2pX\u00CD\u00CD\u0086\u00B2Y$\u00EAZ\f[T\x03d\u00B8\u00E0K\u00E5\u00D0\u0082\u00A8\u009C\x0BZ\u00BA>o\u009E\u00B0\\\u0084v\u00BB\u00D2OJ+;/p\u00A1\u0093\ri\x02o\u0084\u0094a\u00D2\u00A9\x1B\u00FBz\u00DDd\u00F8\u00B4\\\u0098$ \u00DDf)E\u0096\u00D1\x7FXxWyV\u00E4\u00C6\x0B\u00C2\u00B3D\x19\u009Di\x18\x19\u00C5\u0091\u008Cx\u0089*\u00840\u0098l-\r\u00D1\u00A4\u00CE\u00DE\x11C!D\u009F#*\"\u009D\u008C\u0082\u00B59-n3F\x11\u00BDxs3\u00B6\u00BE\u0091\u00B7!\x19JC\x12u=\u00B0=\u00BCyf\x7F\x1D\u00AFmS\u00FA2\u00EEC\tIB\x06)\u00BDIx\u008EkT\u00F8k\u00CFV\u0094\u00B4\x152\u00E8\x04\u00D9\t]a\u00C7\u00ED\u00BA\u0085iti~\x1B}\u0094zPRX\u00DF\u00CDV\u00BB\u00C1\u00DA\u0086\x18\x14\u00892\u0083\u00A3\u0092_\u00C7\u00C3\u00B9C51\f\u00EC\x0En\u00CDN7$\u0089+\u00D2\u009B\u00AB\u00ACz\u00B4\u00AAH\u0082\u00DA\u00C2E\x7F[~G\u00A8\x04&\u00B5\u00A9\u0098h\u00852\u008B\u00D8\x01\x05\u00CF\u00EA\u00BAD\bdB\"\u00BA\u00B346p\u00E3\u0098&ae\x14\u008DS\u00A4\u00D6y<&\u00D7\u0095y6\u009F\u009F\bVL`\u00A2\x16<\u009F{\u00A78Z\u00F4\u00D3\n>u\u008E\u00EC\x10\u0094\u0094\\\u00A8\u00DE$kAi=\u00E7\u00F5xy\x02f\u009E\u00A7\u0082\u00A8\u00F2(\u00A8\u00ED@\x19\u00B3]\x1F\fN<$\u00CF\u00F7\u00D6\x17Il\x00\u00C9\u00A8\x14c\u0080fsX\u00D4\u008D\u00D6_(\x00\u00C8\u008B\u00B1\b\u00BDt\x067\u00AB?\u0080\u00FA\u00AA9\u00AD\u00FE\x04\u00A1\u00CF\u00AAe\u00C0P\u0089\u00B2\x07\u00F8\x12\u00B3\u00B00\x1E\x04\u0093\u00AB@\u00ACv^\u00DA\x10\u00E25^\x7Fe\u00DB_\u00B5\u0087\x18\u00B8\u008F\u00E9\x0F`\u00B8\n\u0088\u00EF*\u0081\u00BE\"\u0080i\"\u00E0\u0093\u00E4\u00F7\u00FA\u00F4,(q:\u00AF\x1B\x16\u0097\u00CDTn\x13lq\u00DB\u00C6\f\u00BC3\x17\x0END\u00DDR|;C\u00A9\u00EF\u00DD\u009Eayn\u00FAw\r\u00B8\x1D\u00D3\u00B0\"\f\u00E5\x11\x02\u0083EXO\u009E\x0E\x03\u008A\x01\u009D\u00D1\u00C0\u00E0}*\u00EAR5\x04\x07\u0090\u00C7\u00F5\u00F0:Z\u0091\n\u0093\u00FB4x\u0086\u00D10OB\u0085\u00B9\u00E8\u0089p\u0087<\x06\u008CIc\u00EC\u0082\x18\x07\u0099\u0092\u00F5\x05\u00AC\u0085\u00C4\u00A1\u00A8\u0084Y\x1E\u00D2\"\x12%\u0088X\n\u00C2\x06AtH+\u00F8Z\u00A5W\u00EB\u0092\u00C6\u00B6|\x04\u00CCNHOi\u009B\x0B\u0096Ld1\u0080\x19\x1Cs%\bJ\u00CC4\u0094y\u00BF\u00D7@\u00A6'/\u00F7\u00D6\u0083\x0BE\u00AC\u00FA.b\u00B3\u00A6\u00DE\x00\u00DAt%!7\f\u00C9YZ\u00CA\u00B2\u00A1\u00D56\u00E2\u009Cx\x1F\u00BB\x1B\u00B2^C\u00AB\u0095X.E1N_\u00AE1\x18\u0094\u009D)\u00E6$1\x11\u00ED\x0B\u00BDx\u00A9\x06sf\u00FE\u0084\u00EB\u00BE\u009C\u00A7\u00F0\u00C0\x14\u00F3\u00D8_\x7F\u008C\u00FDw\u00AF^\u0081\u00F9\x13\u0088\u00A8C\x11\u00FC\u0080T\u0083a\u00FBj'\u00B0\u00C6$(\u0095a\u00BDv`\u00A0\r\u00E5.\u0088\u00F0|\u00D1\u00E7\x1B\u0083H\u00BBBG\x07\u00FA\x04\x16\u0084\u00A2\u00C5`\u00E5\u00BD0\u0093\u00C1\x12\u0089\x0En\u0087-\u00F3X\x0BB\u00EA\u00F9\n\u00C5Pz`T\u00FF\u00BD\b\u00BC\b^\u00E3\u00E3\u00B9\x0E\u0084aT\u00FD\u00DC2\u00A48\u00E2\u00F8k>\u00AC1\u00A5\u00F5\x1C}\u00E3\u00BD|5\x1A\x02\u008A\u00F2'Q\u00FB\u00A4\u00ED \u0096\\\u00C4\u00DC\x05\u00E3\u00AB:\t@\u00B7'\fhWjFCz\u00A9\x06\u008B\u00C4`\u0084`\u00FD\u008Ez^\u008Cjt\u009F\u00CDY \u00C2\u00D4\u009F\u00C6\"\u00AE[$\u00F1\u00A4\u00D8g\u00DB<\u00A4'w\u00FB\u009B\b\u00A3\u00FAd4\u00AC\u0095*\u0082$H1\u0087\u00B9\u0094!?Z\u009F\u0094\x03\u0080\u00B4'd$\u00A1\u00A2\x1Fy\u00BE\u00A9#\t\u00BFQL\u00C0CZg\u00A2\u0091\u00E6M\u00F7\u0091Q\u00CF\u00C1\u0098\x04\u00A4\u00E1\u00CF%\u00E53\u00DD\u00BAT\u009DDI\x19\u00FB1\u00A2\u00AD\u00E3(\x03\u00A8n \x14\u0093%\u00E4\u00C5L\x06\u00E0\x17\u009E\x1F\u00CCL\u00F75%y\u00B1\x7F\u00A3\x1B.i\u00D0\u00B4\u00CB\r\u00F6\u008B=\u00C2a\x0F\u0082\u0087\u009B$\u00F1\u0080\u00BC\x04k\u00E0\u00D0\u008E\x0E\u00E3k\u00FF\u00C5N\u00CC\x05\u0092\x01y\x0E\x1Aq)\u00A2\u00CE\u00BA\x0E\u00AFs\x1C+\u00C3 Ax\u009A\u00C3Je\u00F0\x1E\x06-\x02VnEh\u00CFKTbC\x05\u00F5\u00A9\u00DC\u0086\u008E\b\u00C6\u00D2\u009E\u00D6c\x10\u00E9\u00C6\u00C5\u00A2\u00F6\n\u00AEG\u00BD\u008E\u00DA\u00EA\u00CD\u00BAjs\u008Ai\u00E8o\x15N\u00D5}\u00CB0\u00B9\u00A1*\x1A\u0097$7\x14\u00A3\t\u00A3\\\u008D'\u00CA\u00C0\u00C7\u00DF\x17\u0088}X\u00CE\u00EC\u00EE#\u00B20\u00AD\x10G\x19@\u0081\u00B2\tt$P\u00C5<s\u00EC\u00F97\x17\u0099`3F\x0B\u00CA\u00D1\x02\u00AD\u00DF\u0093i\u00A0\u00AA7\u00F7\u00D0c\u00A7\x0B/\b\u00A8\x13\x0B\u00AA\u008D<f\u008B\u00E1\u00CC\u0093({\u009B\u00D6\u00FC\u00F1\u00A1&\u0088\x18\u0095\u00F8\u0093\bB:~\u0087\u00D3\x0B\x02A\x12\u00F7\u00DA\u00973\u00C3\u00A0\x17\u00E2T\x7F\u00A0\f\u00A7\u009A\x07\u00AA\x15\u00CD8X*\u0092\u00DA$\u0083\u00EC\u00A8g\u009E\u00FA4\u0085b\u008A\u00F9\u00D5\u00FC\u0083\u00FD\u00E6:\u00CE\x00%\u0090\u0099pL\b\x01\u00B2=\u00BA`\u009F\u00CAX\u00C3N\u00F8\u00AA]n\u0094C w\u00E3\n\u0081\x157P\x7FK\u00DAWA\u00D4\n\x15n\u00E7`\x00g\u00FBB\x12 \u0089\u00B8[\u00C1\x13\u00F2\u0091\u00D0\\y\r\u00ED\x1C\x11\u0093\u0088z\u00AA/\u00EB9kt\u00E3\x1B\u00A3\x15,\x0B\u009F\u00BD\u00B3\u009D\u00FE}\x1A\u00B2\u009A\u00814\u00CE\x0F\u00D5Cd\u00E6\u00E8h\u00A2#\u0095\"`\u00CF\u00BE5\x14\u00E0;\u0096\u0082\u00A3}n\u00BC\u0098^0\u00C9\u00F5zEN\u0086}\u00B0R\u008B\u00A6\x01\u00C3\u0099\u0086D\u00FB\u0087\u00B6\u0085\x1B\u008C\u00B0L\bl'b\u00D0k1\u00EC\r.\u00E03\u00C1\u00B3\u00AF\u00AEg7sQ\u00EAx,p\u00AC\u00ED\u00B3\u00CDe3\u00D5\u00B6\u00A0W\u00F8o0\x12\u00B7\u00A0\u00B57\u0086\x10L\u00CB\u00D7\u00EB:u\t6\u0083L\u0081\u00C7M\u00CF\x10b\x14\"\u00DD\u00AE\u00CB[#*1\t\u00F5U\u0081F@\u00D6V\u0090\x04\u00AFU\u00C4\u00F6W&\u00E7\u009C\u00AF*\u00ED\x1E(b\r|j\u00ED\u00B2?\u0099ZM&QQ\u0099\x14\x1E\u00ECS\u0096^\u00CF\u00FE\u008A9\u00D5\x031\x18ps\u00C1\u00EE\u00B8\x00\u00CEb\u00A7\u00A9\x16\u0094\u00C7\u00C0Z\x1ArL\u00F7~\"\x1E\x12\x07\u00FF\x1E\x103\u00E9D\u00A1v\u0081\u00DBR\u00BD\x13\u00BC\x18@\u008B\x12\x1Bb.h\u00F7)\u0089\u00BEz\x0F\u00A2\u00CD\u00AA(\u00DCT\x16\u00FE\u00BE0\x05\u0096m[\u0092\u00B1\u00BC\u00DA\u0089\u00B6hg\u00C0\u008EaLP\u009A\u00D6\u0098\u00A1\u00D5=Q\u00BB*\r\u0096\u008AO\u00B5\u00A91\u00C3\u00D4\u00E7\u009B\u009B\u00D2NsR*\u0084\u0088I\x06H2\x03\u00AA8\u0092*1\u00E6bH#\u00DB\u00D2\u00DA#\u00A2\u00CBHR\u00CD\tK|i\u00CDJ\u00FA\u00BAQ\u00CA\x19J\u009F\x15aI\u00BA\u00D1\u00FF>\x0F\u00C0\x0F\u00E6\u00F4\u00CB\u0088\u00E7\u00CB\"\u00CD\u00EF\u00DC\u00AF\x00\u00CE'6\u008F<W\u00D08K\u00C9&\u008C\u009E\u0081D\u00E0'\u00F7\u00A1\u00B7MU\u00D8\u00EF\u00C3\u0088<\u00A8\u0098\u00A3\u00FA\u00A1k\u00CA\"o\u00D5\u00B6\u00AA+\x1DF\u00D5\u0084\u00E3\u00CF\u00B1\x10\u008E\u00FB\u008C\u0080\x12\u00F7\u00E8\u00FB4hC\x141\u00BA\u00D1\u00D0\u00CB\u0098\x06\u009FG0\u0092y\u00B0M\x12\u00FA\u00BC@\u009B\u00AFV\u00F5\u00A6\u009A\u008Bs\u00B4\u00A1\u00B5\u00D8\u0089\u00AA\u00B3\x00\u00C8\u00C0kto\x0E=\x13\u00DEO6\x05\u00B9#=\r\u0090\u009A\u008B\u00B2\u00D1\u00EC1\u00B5W\u009C*\u00AA\f2,\u00B6x\u0082[\f@n;\u00DB\x1A\u0091U\u00E1\u0092U\u008D\u00E8\u00D6\u00FB3t\u00D6=\u00E46\u00E4\u00C3V\x1F\u009A\x11\u00B3\u00EC\x10w\u00BD[\u00DF\u00DA1_>\u00B1\u00DE\u00B338 \u00C7\u00B4\u00B6\u00D9\u0088\u009E\x10ZRZ\u00A1\u00B8\u00BA\u00B7\b\u00BBB\u0089;\u00F3\u00AA\u00F7\u00D9\x13R\b\n\u00ED\u009CB\x1F\x15\u00E2\u00DA\u00E6\"\u00D5#$\u00C3\x0En\x7F\u00F7\u00DCd\u009F\x18tu\u00B4\u00DF\u00B6\x06\x04\x16\u00E9\u00DA\u0099A\u00FEnPH\u00B2R}\u009D\u00AB'\u00A5?\ne\u0094\u00BB\fm#\x11\u0090N9&\u00EA\u00D3\u0096\u00CC\u00C3\u00E0MeX\u00AA&w}\x1D\u00E5A(C\x1C\x19\u00BF\x0B\u00B2{k\u00B7\u008FO\u00D0\u00B9\u0090Cm\u00F7&)\u00DA\u00FA+i\n\u00ACs\"i\u00DB\x11M\u008B\x17.\u0084\u0081\u00EAgJ\u00E8\r\u00E5Z\u00D4\u00F9\x1AKC4hm\u00A7\u00D1\u0091\u00F7q\u00CF\u00AB\x01\u00CE7\u00D6\u0082\u00ED\u00D5\u00D7\x1Ag=\u00ABz\u0099;\u00F6\u00F2&\x10\u00EAZ\u008B3\u00F0N\u00C0\u00D2\u00C9iSh6\u0080\u0095\u00D8\u008B8V\x03\u00DEh\u00BF\u00AD\u0086\u00C2(\u00FE\u00C0\u00EF=\u00F3q\u00F1\u0099\u00DF\u0086\u00DD+\u00D3i\u0086IC\u00B6}\u009B\u00F8\x1A\u00D3\u00D0\u00BD\x16\u00B8\x1B\bia\u0080\u0086\u00C6\x14\u0089\x1A:C\u00B0>\u00B6\u0090\u00D6JbN\u00C6`\x1D\t4\u00B5\u00C4:\u00E3\x00\u00AA\u00DEC\x11\u00B4[p\u00FE2\u00A06\u00F4chc\u00D4\u00B8\u00CFh\u00A7\x10\u009B\u0082\u0094\u0092q%\u00E1\x17iW\u009F<\x17J\u00FCNK\u00BC$\u00E0ls;\u00AFR\"{JAk\u00EE\u00B0\x18\u00AF\u0082~R=\u00C88\x04C\u00E8\u00C1\u00A3I\u00C7\tO\u00EF\u0092\x0E\x02i'\u0090;S)\u00C6\x1D\x0E{\u00FB\u008D\u00D22\u00E3\u00F6\x1BS0\u00CE\u0099{\u00B5+\tj\u00D9_\u00D6\u0093\u00CCpl\u009E3\x0F,AH\u0089\u008A\u0090\u00CC\u00A8\x18\x0E\u00C0\x1D\u0086O\u00CCc *@\u00A9oV\u00F7Y\bY\u00E7\x04\u00E37\u00DEn\u0099\x07^\x13)\u00E5\x03!\u00D4{\u00D4g\x13n\u00CB<i\u00ED8vw\u00C5\x12h\u0091U\u00A8\u00AF\u00A5\u00AA\u00D0\u00A5\u00AF\u00D4\x02!\u0082\u00BA\u00B6\x16{\u008F\u00D0[\u0099\u00C3\x1A\u00C5\u00C8c\u00860\u00C6,\u008CB\u00C4X\u008B\u00AEt\u00F1.\u0095O\u00CC\x05\x15\u00E5\u00D8\u008D\x7F8\u008D\u00A1\x18A\x11h$\u00ECt\u00E2\u00D6y1\u0092\u00C8h\x17\u00C4\x16r\"Wn\f\u00F2\u00B6G\u00A2\u00A2\x0E\u00B3O.oB\u008B\x1E\u00EC*\x06\u00D6h\u00C58\u00E5\u008C\u00BAb\u0087)\x03U\u0092\u00C5\x00I\x0FPcrg\u00C6\x1C\u00BC\u00C0\u00B2\u0084\x00\x18\u00B9\u00B6\u00FA\u00B8k\u00A1I\u0092\u008B\u00F9\u0088\u00A0#Ym\u00C2\u00EC@5\u00D6\x18m\n\u00DBH\u00B5\u00A2\u00F9\u009C\u00CF\u00A8\x11\u00B2\u0091Xl@n\u00C2|\u008Eb\n\u00D5X;\x1E\fhbFb\u0097\u00F8\x17\x06\u00EB\x16\u00C4\u00F8-#,\u00E3\u00FE%\u00B0jL\u0080lD\u00E9\u0093\u00E1X\u0088\u008A\\\x1E\u00A6\u00F8\u00DD\u00AD\u00DC\u0099i8t\x07\x18\u009B\u00E2\u00D9g>\u00F7I\u00E9\u009D\x15\u00A3f\u00CCa\"\u008B\x18gHp\u00AA1I\u0093\u00AA\x18=\u0081\u00A9m\u0083\u00F5,jz\u00CA\u009D|i\u00AA\u0092\u00CE\u00A5\x10rC\u00D6\x0172\x06\x18v\u00C3\u00D3p\u00BC\u0086\u00F2\u00D5\u008BH\u00EFH42Y\u00F6\u00CF\x04\u00A6\u00C9\u00EC\f\u00E4,\u0095\u0081\u00D5hhK\u00DB\u00D9\u00A6\u00A4Z+\u0096R\u008Cde(\u008B\u0094O\"9Cr\u00D4\u00B8\x14\x1F\u00B1\x18\fW\x02[s\x1Ft}\u00EB;\x1CU\u00C6M\u00C7\u0081u\u008C\u008D\u0090\u00DB\u00B1@\u0081M\u0092g\u0088\u00F5y>8\u00FF\u008B\u00AA\u00D6\u00C7X\u00FD^\r\u00B75\u008En\u00C8\\6\u00B4q\u00A5\u009DG;\u00C7\x04F\u00A3\u0087\u00DA\x01\b)\u00D5\u00D7\u00A4\x1EJq8*\u00E3\u00B0\u00E5\x0E$\u0081s\u00B1q\u00E1\u00BB_P6\u0088\u008E8\u008Cs\u00AAx\u0085K.\u00CE0\x18N\u00C4\u00EE\u00C9$R\x17c\u00D0b\u0096\u009D \u00CE\u008B>6\u00FB\x01\u00A3'=\tJ\u00D7n\u00F5\u00BB\x18\u009D\u0088\u00D4[\u00FB\x18\b\u00DB\u0081\u0098{C!b`\u00CE\u00FD,\u00F3\u00DE[\u00EB\u00CFl\u00ED\u00F5\u00ECM\u00D9I.\u00EC\u00D3V\u00EB\u00C0\u00F5tf\u00FE\u00BD0\x10\u00D5[H\u00E9?\u0090\u00B9HY_\x02\u0081\x10\u00CE\u00F3\u00A5k0\u00E1Y\u00B3\u00E2&\u00E6\u00D1\f\u00BB\u00F9\x13p{\u008C\u00C8](\"\u00A8\x14f-Z\u00E9\u00AC\u00DDfp&\u009A\nI^\u00A0q?\u00B7\u00BB\x13\u00B5\u0098E\u00FC\x0Bt\u00D7\x04\x01\u00E9\u00AAL\x03\u009A&n\u00CFI\x14\x1F\\\u00DC\u008A-7\x02\x19\x07\u00B0\u00A1x\u00A61\u00BAqE\x18\u00854jl\u00B91K\u00FE\u00C6\u00BE\u00B8\u0082BH\u0084\u00E7LJG\u008EC.AW\u00F2V\x1B\u008B\u00E7\u00F9\u00B4\x0Bd\x7F\u00DA+\x11\u008F^\u009DY\u00C4\u00F5\x13*\u00ED62\x0EA\u00F1\f\u0086\b\u0081\u008E\u0080,\u0086\x0ByT\u0082\x0E\u00C1S9\u00F6\x15\u00B02\x1A\u00A1Sh\u0098\u00C7b\u00B2\x03>[\u00A5\u00E1\u00D4\u00ED\u00D1\u00E6R\x12-qs|\u00FA\u008E\u0089\r\u00C3\x0E\u00C08\u00B3\x0E8v>\u008A\t\u008C\u0090\u00E21\u00C7A\u0094Q\u00D2\u00ED\u00C8\u00F1$\u00F15Cd>\u00CBD8!\u00AB]\f\u00C0\x0E\u00B8\u008A\u00C6\u009A%\u008A\u008859\u00AB\u00DF\"H\u00F5'%\u00BA\x18\u00CFF\u00E8\u00ADJ\u009AD\x15\u00D6\u00F7\u00FE>\u00C88\x1D*\x18\u00EBv\u00C4F\u00B9\u00A9rq\u00D2\u00FDB\x06G\u00D5,\x11\u00DFd7\u00CE\u008CH\u00EA\x16\u00E9t\x1C\u00A8\u00CAI\u009C\u00F7dH\u00D1\u008Fb\u00A2\u0091n\u00EE\u0089\u00D4.\u00F0}\u00C3>\u00FC-\u0098x5&\u009A\x0F\u00B2$J!\x04[\x18A\u00FD\u00DEc\u00F3o\u00FF~\u0092\u00D0:O\x12\u00C0z\u00FB\u008DI\x106.\u0081?\"\x1EB\u00B1\x18\f\u008A\u0090\x00\u00DCfT\u0092\u00BEg\u00E6e\u00F9\u00DB\u0099\x19h_e\u00EFR\u00D7\x10\u00D2\u009AR>\u00FAM\"\u00B0\u00B0$[O\u00AA\x02\u008A\u00DB7\u00A8\x1E\x0B\u009E\f\u00D1\u00BB\u00F1Q\u00F3R\u0084\u00AB\u00E7057h\u0086A\u0097qp\u009C$\u00FEj\u00D0T\u00F9\u0090\u00F2\x12\u00898L\u0086g\u00C0\u008D\u009F\x01\u00BBG\u00D2m\u00E8\u009CA_q\u009F\n\u00F6!JH.\u00ACE}\u00C0\u0099~-\t\u00CF\t\u00E3\u00B3\u0093t\u00F3x\u00B6)M/(\b/D\x00\u0082\u0089\"pK\u00E9\bd\u008CC\u00C2l\u00AD_y\x05\u00D8'!\u0097\u00A6n\u00B8\fo6I\u00E7b\u0090Z\x1B\u00ABj\x14\x0B@\u00C6\u00CB#\u009E\x7F\u00D6\u0085\u009C\u00C8\u00F8\x1C!\u0082\u00F6\u0092Z\x06\u00CCl-\u00C7\u0092\b\u00A4I}Ix\"\x15\u00B7\u00A6\x1A\u0098\u00A4;\u009F\u0089]\u00F8y \u00D3\f\u009C\u00CFhr\x1E\u008D\u00B9'\x14\x02\u00EA\u00A3\x00\u00DF\u00A2\u00E6\u00EF\u00D7\u00D8\u00DF=\u009F\u00C5\u00BF\u00D9_\u00E4$(W\x00(\u0088,\u00F8\u00D9\u00D0\x02\f&c\u00CF\u009D\u0085N\u0080\u00B2G4x\u009F\u008FD\u00AAC\u00AB\u00B8l\u00ED:\u00C3\u00C2hV5\u00A11\x1C\u008B\u00F3K\u0092\u00EB\u00DCv\u008D\u00EE\u0097\u00D2U\u00D7v\u00C4\"\u0086s\x1B\u009E/\u00E3q\u00A9\f[\u00B9\x11\u00B3\x1FE\u00A4\u0082\u00BF1w\u00C5XJ\u00BF\u008F\x07\x1ALA\u009F\u0080v|\u00CE\u00D4i\x17\u00E3;\u00ABa\u00BC\x1F\x06k,\x0E\u00DA\u00F9d\u00C8\u00EC\u00DF\u0089N@\u00C9\u00CF\u00AD\u00D8\u00C5\f\u00E0\u00F2\u00E8\x19\u00B4\x11\u00CB\u0096\x06C\u00DEILB\u00EA\u0084k>\u009C\u00CF\u00F0\x12\x12\x06@\u00AE4\x12\u00A0t\u00E8\u00B5\u009C\u009A$\x17\u0089/\u00E1{\u00F4:R\u0090\u00C5t\x1C\u00A52 \u00EE\u00B3\u0084\u00F6\x1E@\u00AFS\u00ED\u0092\u00B0\u00C6\u00CF=\u00A4\u00A0\u00E2\x07\x16Uc\u00C0-$\u00B1\u00A2'\x0BY\x18\u00F9\t\x05\u0094ww\u00A8\u00D6\u00D6\u0081\u00CEF\u0096\u00D2f\u0098|\u00AE\u00CC$\u00A6\u009B\x16\u0086e\x0F\u009BZ\u0093\u009CS1\x0E\u00F7Z\u009F2V\u00E2\x06\u00B5?x\u00F7Nh\x1E/\x10b\u00E8q0\x19\x0B1\u00D5\u00FF\x15\u00D5\u00C7<\x1D\u00B8\\=\u0089\u00FD-\u0097\u00A7\x1E\u00DDN\u00DE\x00\x00 \x00IDAT\u00F1\u00BB\u00BB\fL\u00B5\x11J\u00ED\u00B8\u00AC\x12\u00DF\u00AB\u00E4W}\u0083U\u00BA\u008E[\u00C7\u0096\x14hM&\u008D<\x0Bj\x00P\x04\u00D3\u00A5z\u00F5\x07\u00AD\x1D\x11\u00D0mD\u00D2uz\u00DD\x13y^\u00D5*\u00A0\u00AE\x7F\u0087\r\u00E2\u008Cp\x1C\u00C0AC\u00CC\u00E4#\u009BV*\x04\u00CBg\x06\u00E1\u00F2\u00BCE\u00F2C^\x07\u00E4xT|uRzn\u00A97\u00F7\u00F9C\u00F6\u00C5y\u00CF-\u008F\x15\n\u0098\x10\u00BB)\u00A4T\x1E\u0085x2\rNaN`\x10\x19d\u0099/\u0099\b<l\n\u00A9:\u00F4k'\u008A!\u00A4>k\u0094L\"\u00A4\u00C9\u00D3\u00A5\x1B\u00EBaI\u00AF\u00F6\u00D6\u00F7\u00F8{\u00A9]`\u0093\u00F5!b\u0081\u0097QO\u00C7b-\u00B8]\u00E0C#\x0F\u00E2\u00B4\u0083\x062\x11\u00B8\u00ED!ai$\u00B4\u00B4\u00BC\x1D\u00C0T\x7F\u00D0\u0088\u0098\x10\u00DC\x1C>\u008E\u00C83@\u0084\u009D\u00C3z\u0081\u00FD\u0099\u00E0#\u00B4\x0B\u00A3\u00BCi6\x020\u008E\u00C4\u00E5Z<\u00F2:]\u00EC\x12~\u00C3\u00E3^D\n\u00B1\u008E\u008Ed \u0081\x00\x0E\u00C0\u00E9\u00C9i\u00AE\u00D6\u00F0\u00D6\x1C\b;\x00i$\u00A8\u00BF\x18\u00B8\u0091\u00F1\u008DIM\u0081\u00E3\u00CD\x02\u00AF1\u00AEy\u00EF\x19\u00EC\u00D7v\u00EF0\u00EC[-\u00BC2\u00AA-F\u00C5\u0093\u00AAp\u00F7q\u00CEPOD\"\u00F2H\x04 \u00A2N\u00E9p&@\u00B5S\tI\u0096H\u0080\u00CB\u00DB5\u00E8\u008E\x04z\u00DF\u00F8\u00DB\x12dt\u008A\u009C\u00F4\u00DB\u00CC\u00E0.t 2;<vh\u008A\x1C>\u00E4\u00A2V\u00C4\u009F\x02\u0092\x02\u00F6#!\u00BE\u00F2\x0E\u00BC3\r\u008D\u00B3y'\u0082\u009E\x14\u00845R\u00AD\u00AA\x10k\u00A9\b\x1Di\u00E9\u00DFb@\x07F\u00DA\x10\u0084\u009CD\u00F8\"tAqkLAE\u0084\u008D\u00F41g\u009B\t\u00AD\u00F1\u0094\u009C\u0092Z$\u00EA\u009CoI5\u00B9\x1D\x19\u00B0\u00B4\u00B8\u00E6\u00AER\u00EA\u0097>n\u00EA\x14*\u00ED8\"h5\u00D7\u00850t/\u00F5g\x12\x1E\x13\u00C63mX^\x12\u00EBF5\x0F\u00C4\x12*X\u00D5\u009DR\u00AB]\x00\x14\u0092\u00A1\u00B0\u00B2=\u00DAm\x06\u00DAtK\u009E\u00FBF\u009D\u00BFl/D8\u0089z\u009A\u00D1\u00D1UR.\u0098\u0099LD\u00B2\x1D$\"\u00A0\u00D1/\u00D7h\u00A2\u00B3\u00A62\u00A5q\u00F4\u00E0:\u0092\x11Q\u00CF\u00A6\f\u0099I\u00A3i\x006\x1C\u00D7\u00F7\u008F\u00FDm\u00E3\u00FE\u00D7\u00CD\u00F6\u00B7\n\u00A2O\u00EC)a\u00CE\u0092>\r%-\u0087\u00A1\u00A4\u00FAI\u00B2\u00B1\u00BE\u0082+\u00966\t\u00B1!\x04=X\u00EB\u00B6\bU\\\u0092*\u00D1\t\u00B4\x19\x0BA\u0083\u00DEyS\u00D8\u009C\u009C\x13a\u00AB\u00CF\u00DE\x1F\u00A6E\u00F1\u00D0&Y\u00CF\x16{\u00F5\u00E3\u00B0\u00C9\u00EB\u00A92\u0088\u00E0\u008C\u00BBG\u00D9`\u00C1\u0093\u00D2\u00F5E\u00DC\u00F2\u00F9O\u0093{\u0090\x0B\u00DD\u00CB6\u00A0\u00B1U\u009D\bd\x1B\u0089\x10P\u00F5\x1A\u00A6\u00D3]i{\u00DE/\u00B2\x0E\u0082a\u008E6\u00BF\u00EE\u00E1M\x00\u0099\x02\u00BC\u0098\u0084+j\u0090\u00BE\u00F7aB\x10|4\x0E\u008C\u008C\u008C\x03\u00FBY\u00D1vK5\u00E0)\u009D\u00DC\u00A1\u00F8yo\x121\u00D6\u0085\u00D3\x1F/\u009B\u0080`>\tc\x16\u00B3\u00E9B&\u00B2\x1B=\x16s\u00EA\u00DE`?6\u00C0o\u0088j:\u0081\n\u00F6\u0084\u008A\u00E0\u00C9h\u00DA\u00DA\x13\u0094O\x042\x11\u00C8Dc\u00D5\u00B3!\u00D7\u00C4\u0085\u00CB\u008B\x0Ec\u00F7\u0088sp!\u0088#\u00EF\u0095\u00C8\u0085\u00AB+.\x14S\u00B8\u00D4\u00FD\u0098\u00C3a|\u00E6\u00F5)b\u00A7A\u00D7y\u00FF\u00B4?hN\u00B5\u0084\x1Bs6Gz>8V\u00938cL\u00CB\u00E4\u00D6\u00C4\u00C4\u0090\u00C0\u00FD{_\u00DF\u00AFa/O\u00C7[{|\u00FEm\x02+i\u00EAw\x11\u00DFI\u00C7\u0097q0 \u00D5\u00C6\x07\x06fF\u009E\u00DC\u0082\u00E9{o\u00EDf\u00C1\u00CC\x15\u00A9\x14\u0091i\u00D0gTq\x1B\x15\u00E8\u00BC\x1E<e\u00B0X\u00F9I\u008C\u00B85\u00DEU\u0082\x0F\u00DC \u00F6V\u00B8\u00B1`\u009A]]\x00\x1F\u00E0a\u00F2P\u00AFLAEP\u00E2A7\u00E3`{\u00A2\u0093s,\u00D5\u00C2\u00A9N\u008Cv\x0F\u00C0p\u00B18\x16\u0089J\x01\u009F\u0095\x04\u00EB\u0090-\u009D}\u0093\u0094\u00CF\u00C5&[\u0082\x07\u00C1\u00CB\u00FEFUH\x06\u00C5\x0B\u00E9w\u00F0zxEU\u0084p\u00B7\n\u008AIUAL\u00987rC\u00BA\u00BD2\u00848\u00CEq\u009B\u00D4\u00A9%\u00A1(=\x11\u008B\u00BA\u009E\tPu\x1D\u0095\u00B4\u00D6\"\u00FF\x00\u00A4\u00A5\u009D\x0B>\u00E3\x1B\u0092hX\x17\u00D3I\u00B4\u00DE\u00CA\u009D\u00A5\u00BD\u00A33\u0083b\x14\u00E9\u00E2K&\u00A3u\u00DDvJ\u00EA\u0082\u008A\x04I\u00B3-\u00A5:\u00D7\u0092\u00A9\x1D\u00A9%Z\u00F74n\u0082\u00D0\u00DFG\u00EB\u009F\u00DEZ\u009B\u00B2\u00BBL\u0084\x01\u00B7?U\u00F5Qs\u00A0\u00B1\r\x18.\u00E4\u00FEuO\u00F8\rF\x06\u0085\x05Z\u00BBl\u00F7_\u00DE_\u00C3\u0093_\x06\u00AE?P\u0092\u00AC\x11\u0099w\u0082\u00EAj@'\"\u0085\x17sj\u00F3\x1A>hS\u00C4\u00E1\u0099P%%\u00BA\u00F1O\u00EA\x06\u00D6~t&\u00B0\u00F4\u00A1\u00A9/KlA\u00FB-\u00FB\u00D1\b\u00DE\u00CE}\u00E9\u00E39\u00C7\x15\u00C42<|\u00CFi\u008E\u00E2\u00A8\u00A1.\u00B8\u008F\u00F6H\f\u00B7J\u0098\u00F9\u00A0\u00B4\x16\u009D\fLZ\u00A53\u00C1\b\u00C5(tLRbM+\u00AF\nK\u00B2\u00F9%\x12O\u00A6\u00B0G\x7F&p\u0099\x1C\u0085P\x01b\u008D\u008Eiw \x044\"\u00E3gg(\u0084\u00E0~\u00BAq\u00EEUH\u00C4\u00B5Z\u00F7\u00E99\u00C8\u0085\u00ED\u0094\u00C8\x07Ty\u00B9\x16\u00B2\x18\u0087\u00A4\u00DC\u00D1\u009E\u00A9\u008ESM\u00A1\x1A\u00E2i\u00C7\u00E8*CAe\u00F3~\u009D\f\u0097V\u009F\u00B2\u00A2j0\x0B\u0083j\u00C4\u00A8\u00BE\u00CF\u00F2\u00B0\x18&\u00D2\u00DB``_\x19\u00F3\u0090\u00A8\u00A3o\u008A\u00AB\u0095D\u00F4#\u00A3\u00B4;\u00C7\u00D2\u008C\u00A8\u008D\u00B0\u008B1\u00A9\u00EFV\u00E3\u00C9{L\u00D8\u0094\u00D7\u00E3\u0080\u00D3\u00F6bhh\u00AB#\u0092|\u00C6\u00CEg\u00B4\x018\u00B0\u008F\u00AB/\u00EF\u00CF\x1D\u00CF\u00BE\u0084k-b$A\u0084kO\u0092\u00F9.\u00DD\u00FA\u00C44`\t{\x04\u0095\u00CBp\x18\u0084\u00E9\u00CB\u00B5'i\f\x04sH$\x02t\u00C8v\u008B\x01\u00F4\u00F7\u0089\u00D9X\u008F\u00CA\u00F3\u00B1\u00DE\u00D3\u00B0\x10Xg\b\u00E3\x1C\u00F5\u00A8\u00E8C\u00DFh\x00Dn\u00A8\u00AA\u008DP\u0090S<\u00A0\u00AD\u00BC\u008B8\u00B38W\u008E%=\n\r\u00B5\u00F4c=\x0E\u00C1\u00AD3\u0083\u00EEv\u00C4\u0082\u00C2\n(\x0E\x1C\u00EE\u00B4bKN:\u00B6T\x11\f\u00F3@Z\u00B9\x0F\t1G\u00E6\x0E\u0089\\<\u00AFAJd\x03\u00DD\u0092\u00B31\u0088\u00B4\x17\x00KJq\u00C0\u00C3\u00B6\u0090I\u00C8$\u0088\u009C\u00B9<\x0F\x15m\u00A7\x05\u00EB\x06k\u00C4\u00E9\u00E9q\x10\x10\u009E\\\u00E8\u008C%@\u00B3\u00EF\u00B0\u00BF\u00D2\u00F1\u00B5\"B\"\u0087\x0F\u00DE\u009A\u00C4\u00F5|\u009A\x16\u00C6\u00C7\u008C\x13\u00E1\u00BArURB\u00F5=\u0091\x02\u00BDn\\\u00EB.\"%\u00B3*$\u00A8I\u00EE\u00F3\u00E6\u00C1\x14\x18\u009E^\u00ABFj\u0091T\x1C\u008E\u00D7d_:`\u00B8\u00D0\u0088\u00ED9'A\u00B3\u00B3\u00BE\u00AB\x1Ax\u00AAb\x07*\u00F6 tC\u0085D_\u00AE\u00AF_\u00DA\u00AFn\x1E\u00BC\u00E8\x0F\u009Ad\u00CC0\u00D7\u00AE\x1A\u009CR\u009F\x11\u00D0\x1B\u00BEU,Q\u00BA\u00C86J\u0095-\u00F4\u00FBn\b\u00C9\x7F\u008B\u00DB\u00AD\b\u00A1\x11\u008E\u00EE\u00D1\u008E\u00DF\u00A5\u00CA\u00DC\u008Az\u00ECD\u00E4\u00AD\u00AF\u00C9\u00A7\u00EFj\u00E7\u00AE7\u00E7\u0084D>\x01<\x060\u00AD,\u00FEQ\x12\u00BDvY\u00DA\u00B1a\u00D2\u0096\u00A09\x14$\x17*qF \u00A6G\x02\u008A7\u00A0\u009B\u00D0\u0094\u00C2\\\u00E5\u00D3\u00CBKA\u00C3\u00A1\u009E=\x1F\u00AD\x02i\u00E1\u00C0&\u00A2\x04m\x01\b\u00C2\u00DF(\u00D9e\n\x18\x0El\"b\tQ\u00A7\u00B1\u009BD,\u00CF\u00C3\u00C1\u00DF\u0086\x18\x04\u0080J8\x02\u00E9\u00DA\u0080\u00C6\x1C\n\r\b-\x00\u008Bz\u00B0d#\u008A0\u00B5\u0096t\u00AE\x0E\u0088\u00F17\u00BD9\u00D7P#\u0080\u00FCI\u00C2J\u00C4E+}\u00CC\x00*\u00BA\u00F0\u00EC\u00AAT\u00BBjC(eFP\u00CF\u0082\x1E\x1A\u00EA\u00F5\u00AD\u00AC\u00F5\x1C\u00D7r_\u00D7\u00BA\u00EE\u0091\u0092\u00ECKC'1\u00A2f\x07\u00D0\x10\x15\u00C0\u0094FC!\x06y\u00E0@\u00B4\u00A3\u00DF\u0091\u00CCW\u008C\u00CE\u00C7\b\x1B\x0F\x06r\x1F\f\x06@E\x0F6\u00F8\u00F6\u00C4\u008B\u00FB\x07\u00F6g~?R\u009C\u00F9\x10\u00D2g\u00DF\u00A5q'\x1E1\x0E\u00E35\u008D\u0098\x15\u00DA\u00ECa\u00B4P6^\u00A9\x07\u00FA\u00DEl\x0E\u00B6\x12\u00BD/\u00D7\u0094G\u00A0$\u00B6\u00CEGNtF56w\u00E2\u00A2B$\u00B3\u00AA\u00BET\u00FB\u008E\u00BB\u0099\x03%2%\u0084\u00C1\u00F0\u00C8\x1D7VL\x00\u00B0|\u00C4Q\u0081\u00BA\n\u00A4H\u008FW\t\u0095\u00A8\u00B1\u00D7\u00BC\x05yN\u00F3\x17\u00EBoC^\u00BFVP6-aLlY\u00A99%7%v\u00D8\t\"\x02^\u00C1E\u0092n\x13M\u00E2\u008B&[j\u00EC\x007{\u00C9\u00A7\u00E2i\u0080\u00D55\u0083\u00CC@9\u00FB\u0099\u00DF\x0Fkk\\\u008CA\u00CFI\u00CF\u008C\u00CF]\u009D\u00F2\x1B\x14,\x16\u00B3\x00J\u008D\u00B8\x0B\u00CA\u00AB\u00DD\u00A6\u00E3\u00E7w]\u00D3U\x11\u009D/\u00EB~\u00BDs\x13Z\u00A9\x1C\u0099\n,\u00F4\u00B2\u00BA\u0090\x13\u00B1\u00A0$7\u00D0}Ozi\u00DD\u00CA \u00DA\u00D5\x10\u00A0\u0082\u00B1\u00D2\u0090\u00C3\u00F9ij\x16,\u00CE3\u00CD\u00CBy.v\u00C0/0y\x1B\u00D2{\x13\u00F3\x1CZ\u00C1%\u00FB\u00EB\u0090%KQ\u00C1D\x0ED:\u00AF>\u00F3\u00CC\u00EF\u00EFo\x1Do\u00FB=;\u00EE\x01\u00FB\r\u00BA\u00AD\u00A0b\nDDEL\u00B91\u00AA\x1B\"vAS1\u00928\x0Ba\u00E8\u00D7Fhs0\x7F\x7F;\u00B5\u00DBU\x04\x12\u00A9:\u00DE\u00F5\u00FE\\`\u00F4\x1E\u009Cb\u00F5\x03\u00EA\u00D5\u00FD\x15\x1B\u00B0\x04?\u00F1\u00FCq\u00BE\u009F\x18\u0081\x16\u00B8U<\u00C6a\u00A5\u009D\x1D\x1E\u00B1\t{\u00DA\x11\x14\u00C9\u00B9%\u008C\u00B7\u00BC\u00F7\u0096\f$\u0090\u00C1\u00C6\u00CA\u00C9e#0\u00A2\x101\x14A\u00D6\u00BEE{\u00CD#\u00AD\u00D2\x19\u00F3 \u00A1a\u0090\u00A4\u00EF\u00C2\x07m\u009D\b\u009A\u00BB{,\x11\x0F\u008F\u008B\u00BC\x03a\u00EF\u008A67g\u008F\u00E9\u00B3\u00CE\u00B6\u00A6P\x02\x19\x11\u0089.\u00DC\u00F0Z\u00D8:\u009F\u009F\x07\u0090\u00A9\u00CE\b\u00B9d\u00CE\u0080\u00A1\u00B6\u00D7b\u00BDF\u00DD,\t\u00B0\u0090e\u00D4/\x10\u008A\u00E9\u0088\u00B3\u0090\u0087\u00DB\u00803\tHi\u00C1u\u009FK\u00AD%\u008E\u00B3\u008C\x7FL.b\u0096TT\u00AF:\u00E0\u0098\u00E4\u00A2\x1A[1\r\u00A3@\u008D0\u00E8\u0089\u00A8\nMF\u00C15l\u00A6:V\x13\u00DA\u00C0x\u00D8`\u00D7\u00C94l\x0B{\u0082]8O\u00D71Of\u0088pkn\u0098\u00CB\u0087\u0091\u00D0_\u0091\u0093\u008AC\u00B0#<C\u00B6sLV\u00AB\u00D2\x1CU\\76\x1Fr\\\u00B0\u00BD\u00ED\u00A9\u00DF\u00DB\u00BF\u00FD\u00FE{_x\u00E2\u00F1\x03\u00BC\u009AK\u00B8\u00A9\t\u00A9\x12T\u00F6b\x11\u00FEz\u00EEB\u00BC\x0B$\u00EF\\3\u00A2\x13C\u0098\x17Q\u00BFQ.ATG:!\u0094\u00C5^\x10\x7F/a\u00CF\u00A7w\u00A2\u0082S\u008A\u00F2z\u00AF\u00BE\b\u009B\u00FD\u00A3y7\f\u00C0\r\x06\x1Ec\u00C3\x05Q#cR\u00A5p7\x1C#\u008A\u0099N_4@T\u00ACB\u00F4\u00F3 \x132\u008B\u009A\u0083\u00DAXe\u0082\u00AEPp\x07'\u00C8\u00C3P\u00FDL\u009DX\u00B9\x11\x1E\x15q\u00B4.\u00E5.T\u009D\u008D\u008B\x03\u00BB#\u00AB\u009C\r\u00A1\u0082&\u00E1\u00C1:~\u00E3\u00E0\x1D\u00A8\x1A\x00\u00B41\u00A4\u0084\u008D\u00CF1\u00A3\u00ED\u00EE\u00A50\u00D1dJ9^C\u00DA\\\u00EB\x01\u00F0\u0099M1\u0099.\u00B5\u0081\u008AS\u00F0\u0092\u0092\u00D9\u00BET\u008B\u00A6[\u00DF\u00B2\u00C87\u00E2\u00F0\u00C95\u00C7\u00F5\u00D2\u0082\u009B\u00A2=qI\x14\u00B1hM\u00A5$\u0096Z#\u00E4\u00A1k.\u00C8\u00BD\u00B8Z\u00C0\u00D5\x12\u0086\u00AC\u00BE\x13\u00CE\x07\u00F3\u0092\x17\u0084B$m\r\x1A\u008B\u00C6\x15\u00EDZ\u00BA@\u0085\x12\u00B8\x1E;\u00D7\u00B7\u00AE\u00FE\u008C\u008A\u008F`\u00BF\u009C\u0088\u00C3\u0096\u0084+C\u0097\x1A\u0097ax\u00EA=\u00EF\x7Fa\x7F\x1F\u00BE\u00EDw\u009E\u00F5\u00A7\u00F1*^\u00BFE\u00F4\u00D2qW&\u00A0A\nz\u0090\u00D0,\u00A4\u00A9\u0088\u00D5\x16b\u0093NG)\u00B7\x18+\u00D1\u00E0XIF\u00F3\u00B6_d2\x1B=\u00D8\u0091\u00E7\u00B9m\u00B1\u00E1\x04\x1A\u00AC3k\u00E7\u00B5\u00FB\u008B\u00D1,6\u008B\u00C6\u00D4\x18Gp\u009B9IZ\x03\u008F)\u00D1\x0F>\u00AA\x1BD$\u00FBt\u00EE\u00DDh\u00B1\u00D1\u00AB\u00B6n\u008F}\x12\u00AA\x16B\u00EE\u00A1\u0080\u00F0V\u00C8\u00AD(\u00BB\u00C4\u00C1\u00FB\x1E\u009C7\u0095r\x17\u00969\u00E0\u00DCg!\u0088+\u0089\u0093\u00EFd\n$\u00A2\u00C3\u0081\u00CD\u009D\u0085sbQ\u0087!\u00D1\u00C3\u00AD\u0098UxCj\u008F\u0089\u00F4r\x04c\x18!\u00F4e\u0088\u009ChFJ\u0084\u0097\u0082k>\u00E8\u00AA\x13\u00F0\u0099\x19T;\u00B9\u0096\u00F4|\u00FC\u00B22\u0095\u0084\u00FB\u009D0\u00BB\u00FE\u00AC6$\u00F5\u008F:\x1FjG\u00D7v\u00C8\u00DDaw\u00CFI\u00A0\x14\u009DB&\u0092\u00EC\x1D\u00D2\u008Bi\u00CC\u0090\u00BC\u008DH\u00B3?i_`\x1E\u00C1\f}\u00DDR\u00E7\u00AF\x1C\u00880\u00B0^\x1A\u0093\u00E9\u00CC\u00AE\"K\x16\u00E9\u009EL\u00D2s\u00CD\u008A)\u00AE^\u008C\x1E\u0089\x19\u00F1\x06\u00DER\u00B9\u00CBs\x11\u00DF_\u00BF\u00DE\u00F0\u00CC\u00F7\x7F\u00FF\u00EF\u00EC\u00DF\u00EDW\u009F{\u00CE\u00DE\u00E6\u009F\u00C7\u0097\u00CCN\u0084\x13\u00D2WD\u00B2\u0086\x0E\u00DF\u0085\b\u00ECD@uLL\x03\u00D9~\u00A9\x07\u00C8\u00F3U\u009C\x05\u00BAg\u00DA)Z\u009B>\u0080q\"\u00EEqF\x05\u00EC\u00D7\u00A2\"4{DC'\u00A9\u00E6\u00C0V;Vcj\u00F2\x1A\x18\x19\u00C0\u008D\x1B\u00EE\u0091x\x0F\u00C4\u00CE\u00CF\u00831\b\u00E5k\x0FUBh\u00C1A\u0081\u00E8H\x15&\u0093\u0094,\x18\u008AQ\u00D5: i-\u00E6\u00A6\u00D8t0\x0F\u00A1\fz\u0087;\u00B6\\\u00B3\u0096h =j$z\u00B9\u00E9\u00B2|V\u00EA\u00FE\u00D1\u00A9\u0083\x06\u00C19-#\x7F\x01_\u0099MC\x06b6zml3\u00D1\u0082\u00D6\u00BD\u00DE@\x11\u00F2\u00B4\u008A~<t\\\u00F6\u00AA\u00B8\u0099\u00EA\x07\u00CA;\u00B2\x18\u00FF\u00FC@\x16N\u00C9E\x1D$\x14j\x14\u00B1!\u008B\u008Ff\u00B2\x15]*!\u0090.A \x19\x0E\f(\u00A6\u00A1\u008C\x7FB\x17\"`1\x0B\u00ADI\u00D1\u0084\u00A4/\u00EA\u00B7d.\u0092\u00C6JO\u0096\u00B4\u0096\u00FA$\x15qk\u00E7\"\u00DBKuqr\u00B51\u00932\x12\u00B38o\x1E\"\u00AAB\u009F\u00DBs\u0080W\u00BF\x01\u00AA\x1D\u00A2\u0083x\x00\x0E\u0083\u00BD\u00FDi\u00C7G?\u00FC\u00B9\u00FD\u00D91^\u00FD\u00E1\u0087\u00FF\u00C7\u008B\x06{wZ\u00F6\u0097\u00D0c\u0094\u00C7`\u00D1\u00E1Hp\u009A\u0098v~}?\u00878\u00EB\u00B7\u00DB\u009E\u0085z\u0097[Q\x06\u00BDx\u00F5\u0089\x13\u00DA8\x13w;o\u00B9\x7F\u00DD\u00A7\u00F4q\u00E3\"\x12\u00E3\u00A0\u009DA\u008CF\tF\u00A7>>\x06\u00F0:\f\u00F71p\x10\x05\u0084aMR~\u00B0\u00F4=U\x05\u00E5<\u00B8\u00BC\x04\u008C3\u00B0\u00B6\u009B3\u00DBQ\u00C9\x14\x00\u00A9JH\u00CD\u00D0k\u008A\u00F0\b\u00DF\u0093&$\u00C4\u00B8\x10f IHJ\u008FYR?a<\u0089\u00D1\x0E$c\u00B0Yl0\u00D7\u00BE\u00DA\x012\x11j\u00B0\u00AD\u0083\u00C7\u00D3\u00D8\u00B8TH\x02\u00D6\u008E\u00C7\u00FD\u00E2\u00FE\u00D6\x10\u00EBh\u009FM\x1A\u00EB\x128l\u0091\u00EC\u0082\u00C3\u00DD\u00D0\u00A6k\x1Ba&\u00D4\u0097\x14\u00A7\u0084\x16\x03\u00CA\u00F9\x1Ad\x1E4\u00BEI\u00E7\u0097\u00AD&\u008D\u0094\u0092\u00BE\r\u008E{\u00CB\x02\u009D\u00DDS1\u00B0\u00D8=|o\u00E3\u00D2\u00B5^\u008CA\u00EAH\u00F6U\u00A2\u00A4\x19/S]Rx6\u00DB\u00C8\u0089\u008A\u00F9\u00B1^/a\u00A1Y\u00A9&\u00BE\u00B4mp\u00DC{\u00F2\u0099\x17\u00ED\x03O\u00BD\u00BA\x03\u00C0\u0093\u00FE\u00E03\u00C0\u00FE\u00EE\u00AA/\u00D0uvC\x05\u00F5H\u0097m\u00A5\u00BC\u00A8\x0B\x17\u00F1\u00DC\u00A5\u00A7\x17\u00F2\u00E8\x16\u00F6\u0085\x11\u009C\u0083\u0087\x12\u0081\u0088@\u00CBh\u00B7\u00A8.K;w\u00C3\u00FD*w\x06\u00F4\u00E2\u00A8\u00F34\u00A6\u00BE\u00FDZ\u00E9y\u00C5\u00C5\x1D\x03\x17\x00\u008F\u00A0\u00C8\u00C5\u00B0\u00F4\x1FV\u00D1\u0081\u00B1\u00ED\u00FBP\u00AF\u00A1\u00E0\u00A2d\f\u00A6\u00DD\u0098\u00C2V\x10\x19\u00F3UeIp\u00DD\u00C4,\u00C0\u00D0hg0\u00AB\u00ECZ)\u00B1=\u0093\u0093\u00FA\x1Bn\x18s\u00F2\u00B1S\u00DEM\u00C31\x01;\u0082\u00C0\u00E6\u0094\"C\u00B67\r\u00F3p\u00C0,\u008Dp\u00CA\u008A\x17})U\u009A]\nd\u00E0\u0080O\u00C3\u00D1\u0098\u0081\u00D8\u00B3\u008B1\u00F1\u00FA\u00CE\u00B4\u00C4p\u0096\u00C8CHB\n\x0E\u008B\u00E8\u0081e\u00B3\x15\u00BD\u009B\x1B\u00B1\u00CEkR\x1F\u0088\u00B5\u00EA\u00CA\"\x14\x01v\u00DB\u0085\u00B8\u00A3\u00DA\u0094\rB\u008C\u00CA\u00EB\\\u00AD\u00AB\fzj*L2 \u00ED\u0092\u00E1\u0080_\u00C1z\u00DFx\u009DCi\u00C84\u0084v.\u00DA\u00ED\"BF\x0B\x13\u008C\u00B9P\x1B5\u00EE\x01\u00D8\rJ\u00BDo\x13\u009ELS\u00C7\x15P\x15(\u00E6\u00F1\u00BDg?\x03D\u00E2\x00\u00DE\u00E6\u00EF\u00FC-\u00F3\u00AB\u00FF0Cd\u00E5Ah\u00D23\u009E\\\u00AF\u008B\u00D0\u00B8\u00A8e`k\x12TO:\u00EA1\rk\u00AArSQ\u00EEHG\u00EE\u00E9\u00CA\u00E5%(\u00CF\u0084e;\u009D\u00C1(\u00B0\u00AA\b_\x0E\u00CFz\x15cZ34\u0089\u0088\u00B2=\u00DC\u00D9\u00A7G6p\u00E3\u0083\x06\u00C4 \u00E8\u00C3\u00C2\u0096p\u00806\x04>\u00E6\u0083\f \u00C3M<\u00A2\f\u00E5a\u0098\x1EhA\u00A6&\x19\x1DG_&'\u00EFA\u00B7\u00B9i\u008DM\x12a\u00DA\u00C0P\u00814N\x06Q\u0081F\u0096\u00C1E\u00DD\u00B8\u00A8\bF\u00E7q-\u00D1\f\x04Jf\x13m\x0F\x00\u0098\u0091\u00A9)\u00D4!\x17dn 2{\x1F\u00B40k\u00ED\u00C7\u00EF\u00BC\u00D9\u0082.\u0082p\u0087[X\u00EByN\x19\u00FBZ[\\\u009F\u0095\x1BA7\u00AF+\u009DZ\u00D7!t\u00FA\u0094\u00C6\x17(|+\u0089\u0099\u00CF_\u008CB\x15\u00A1J=\u0096\x0E\u00CF\u0088\u00C5\u00B4A\u0088V\u00C4\u00B1\x15)(D\u00A2\u00F3\u0080>\u00FE\u00A4\u00B5zp\u00CB\u00BA\u008E99\u008A\x1E\u0092\u00A1\u0095\u00D8P\u0088\u00B4\u00FB%b\fR\u00E7\x13s\x0Btc4>z\u00B8|8\u00CE`]7\u00CF\u00DC\u00FB-\u0080\f\u00E1;\u00E7;?\u00BDM\u00C3\u00DC|!\u00DE\u00D0\u00D7\u00B7\u009C\u00A8\u00D5#\u00D0\u00E3\x15z~\u0082-\u00C7o\x13l'4C\u00ED\x04e\u00ED>\u00D4\x053\u00F3\u00B23\u00A0\u0080\u00F6\u0096i\u009Et\u0083\u009E\x19L\x12\u00BB\u00DA\u0094\u00AAQ\u00F6\x02\u00F5S[\x7FW\t\x00\u00E3sk*Q\x1A\u008E\u0080G\x03x\u00DD\f\x0F\u00B0\u00E1\u00C6\"\u009B\u00E0\u00A0\n\u00B1\u00A11\u0083\u0094\u00F0\u00F1\x1EtgM(\u00E2@\u00F9\b\u00F4@44\u0096\u00E6+\x1A\t\x1DXlj\u00D3\u00C1\u00EC\u00C5\u0090\u00FA\u009E\u0092\f\u0099\u0093 \"\u00E8\u0086\u00C79\u00B5f\u00A9v\u00A0\u00CE\u009D\u00EE8<\\\u0089\u0083\u00F4\x19\f\u00C8\x0B\u00B9\u00CC\u0098\u009F\u00C1\u00FEH5\u0091w\u00C2i(I\x06\u00A0\u00CDX\u00D2 \u00BE2\u0084[\x04\u0082bH\u009A{m\u00BE\u00BA0\x00\x11rJz\x11qAi\u00C1{KI\u00DA\u00A5\u00BC>\u00BD\u00AD\u00BDhgIv\u00D2\u009AHBT8qC\x12\x1C\u0084\u0083kR\u0097\u0085o\u00B8\u00EE\u00D5=\x10\u00EA[2\x02\u0096i[<,\x13\u00BEx\x19\u00E2\u0098a\u00D6\u00FCr\f\u00F1\u00DF\u0080\u00C9H\u0099\u00E8b\u00AF\u00FB\u00BAvyo\u00F3\u00CE\u00CE\u008Eg\u009E\u00FD4@\u0086\u00F0\x1F=\u00F9\u0081O\u00FE\u00AF7\u00F7\u00F1x<B\u00AA\f\u00D6\u00D0\u0081\u0095\u00B4\u00CF\u00E4 \u00D4\u00E2\x15\x11e\u00D4aJv$d\x06\u00D6sW\x14\u00C1cM]Y\x19\u00C8\u00D6R\u0095\x01\x1B\u00CD\u009D\u00D8\x19\x14\u0099\u00C2\u00B8u\u00AFB\x1F\u00B3\u00A1\u008E5\u00FD\u00B9Im\x04\u00C20\u00A9\f9\u0083\u0080\u00A2\n\x1F\"\\\u0090W>p\x05\u00C7Aubs\u00C3f#5\u00BF\u00CDA\u00A2G\x04k%\u00BC\u008C{o\\\\\u00B1\u00D6=\x17Q\x12\u009F\u00D7\x1A\u00E9\u00DB\x13\x18\u00D7\u00CC\u00CC\u008E\u00AD#\u008E5\x15\u008B\u00DD\u00A7\u00D5\u00B9\u00B2\x1F\u00E82\u00D2HT\u00D5\u00B2(\u00A3\u00D6\u00DA\x01=\x14\u00C2Y\u00DDk \u00C4\u0090\u00F6\nh\f\u0084\u00C2D\t[g\x02\u00B3}vt\u00A0\u009BNp\x7F\u0087\u00FA\u00BB$\u00BC\x04\u008E\x17gL\u00A8\u008F *T\u00FF\ny\x04AL\u00D6h\u00A814\"\x17SH\u0095A\u00F7\u00E9u\t\u00F5\u00DCnP5 \u00AC\u00EE\u0093\u00D1\u008E\u0086\u00CA5\u00B8\u00E8\u0089\u00C4\u00BA2\u00E1ON\u0082_h{\u00D8\u00D6{\u00E78v\u00DC\u00AE\u00C4\x14\u00A82B\u00FDew\u00F0\u00D6\u00FFH\u00EFN\x1BC\u00DA=\x06\x1C\x17\u00A2\u00BET\u00EApc\x13\u00C7\u00F7}\u00D7'\u00F13d\b\u00EF\u00C03\u00BF\u00F9\x1D7\u00EF\u00C7\u00A7\u00F7\u00CFB\u00D5{\u0093\u008B%\u0091v\u0088]Du\u0097\u008D\u00C1s9\u00E9\u00A6g\u00DD\u00BE\u00C1\u00F0;U\u0084~^cP9\u00D9\u00FD\u00DC\u00BB\u00FAV\x13\u00A7\x18\nW\u00FB\x19\u00B8THe\n-,}U\u00BB^\x7F\x1B\u00B2\u00FD\u0087\u00EEx\b\u00E0\n\x03;\x0E\u00EE\u0095\x1D}r\u00A7aQ\x01Q\nU\u00E6xB\u00C8\t\t\u00B0\u0090\x06,\u00F4u\u00EA\u00AD}\u00CD\u00A7\u008B\u00AF3\x07.\x11\u00E5\x16\x00@\u00ED\x1C\u0084\"\u00E2N\u00C0\\d\u00B9\u00A1\u00C8A\u00A6\u00E1\u0096R\x1E\u00B3?\u00E1\u00F8\u00ED\u00F0\x16SI/D!\u0094b\n\u00EE\u00CE$)2X\x17r\b\u0082It\u00D0R\u00A9\u00B9&qr\u00F1\u00D4wO<\u00A2A\"P\u0080H\u008CD\r\u00CD\u009D\u0092x\u00BC\u008E#\u00D0\u0082y\u0095\u008AO\u00FBDN^\u00BBq\x164\u00E5D'z\u0090\u00E4\u00BEB&\x1E\u00A5\u00B7@\u00C8\x04\u00C8\u00BC\x01\u00F6\u0081\u00D6\u009F\u00EAS\u0089wT\u00AC\u00CBD\x16\u0083\u0091\n\u0092O\u0099\u00E8\x07\u00ED\u009D\u00AE\u00C3\"\u00EC\\\u00AB\u00F2\u00CA\u00A4-\u0084c\u00B4`p\u0096\u00DE\u0094(U\u00E7\u00CF<\u00C0\u00DB~\u00EC\u0087~\x13\u00FF\x0B\x19\u00C2\x07l\u00FB\u00C2\x7F\u00FC\u00E8\u00FF~\u00F1S6\u009E+X]\u009DM}\u00C5\u0094\x11)\"!|o\u00D2\u00F6\u0096\u008D`a0\u0094\u00E4Y\x0Fad\u00DBqE\u00E9\u00F6\u00D9\x0F2\u009CE\u00A7\u00CF\u00F6\u00D7D\u00A7\u00F2 \u00D4\u00C3Mf\u00C0v\u00D4\u00CF\u00C8\x06\u0095\u00E5c\x14?pF?vx\u00DA\u00CA\u0094\u00C9xz\u00B1@\t\u00F7a\u00B8\u00F1\u0081\u00C7\x00\u0086\u00E7\u00EE\u008F\u00D0>\f7\u00ECS\u0094 \u00A7\u00A11\x13\u0093b'\u00E92>\x16\u00D1\x06\u00D4\u00F7\u00ACc\u00A0\u009D\u00A2S\u00A0\u00BA\u00B6fkk\u0086k\u00ED\u00A0\u009A\u00A1\u009A\u009AZ\u0087\u00C9<x\u00A2)\u00EBq\u00D6\u00B9\u00AA$\x16\u00EAD\u00F4I\u00B1\f\u008Ep[N\u008B\u00DF\u008F\u0089B\r\u00B3\b>J\u00A2\u00F3)\u0092QT@\u00945B\u00D5X\u00AD\u00EC\x16\u00CB\x1E\u008B\x1C\u00BB\u00C60\u0089\x14\u00B8\u00D3r\u008D\u00CF\u00B1\x06(\u00A1\x11\x0F\u009Fa&Tu\x1D\u00BC\u00C1x\x07!\u00B7\u0084@\u00B4\u00A5\f\u00CE\u00B0E\u00CCbP\u00BAG\u0086]k\u00ECr\u00FF\u0089\u00F0\u00F7\u00EC\u0097\u00A5\u008A \u0098$\u009Ap0\u00C2\x04f\x17z\x0ED\u00C4m]\u00B4\u00B5\u00BE\u00BC\x1C\u00A8\u00BD34~]\u00A1\u00B9\u00E6\u00DF\u0099y{pz\x1C\u0097\u00E7\u00DE\u00F6\u00A2\u00FD\u00E0\u00BB\u00BE\x00\u0090!\x00\u00C0[\u008E\u00A7\u00FE\x19\x1C?\x12\u00C4z\u00CA(4\u0085\u00C6D\x03=y\u00C8F\u00C5\x1AT\x1E\x02\u0089\u00D3\u00B5\u00F8)m[\u00F4\u00DF\u0082\f\u00B2\u00EB\u00ABZb\u0094\u00EAk\u0080\u00D4\u008AHFg K{\n'\u00E6\u00FDN\x1E\u0084\u00F4\"\u00901 \u00ED\x12\u00A3\x18x2.\u00E4\u00B8\u0090\u00E3\x1A\u00F8\x06\u0080\u00FB\x00\u00AEh\x14\u00BC\u00B1\u0081\u00DD\u00A3'\x1B\u0098\u0080jat\u009BpL\u0097\x0B\u0091nJ\u00B3&\x17\u00BCT\u0082\u00C6\x1AK\u00A8\u009C\u00A2\x12i\u00F0R4\u00A1\u00898I\u00DC\u0092\u00E0\u00A0\x17B%\u00D3\u00CAR\x191\x07\x00Je\u00F0\u0088n\u00DCH\u009C\\^\u008C\u00A0\u0094j\u00C0\u00CC\u00BC\u0086\n\u00DC\u00A3\x0E\u00E3\u0080\u00A5\u00BD\u00C3y\u00FF\u00890\u00A4f\u00F1V\u008E.\x11\u0081\u0090\u00BF\x16\u00F6lK\u00B8\u008D\u00DB\u008EB6\u00EB\u00F3n\u0093\u0084\x01\u00CCk6\u00AA\u0098\u00FE\u0093\u00C4o\u00E1\u00C6\u00D1\u0087\u00EE~l\x1DZB\u00A6;\u00A1q\u0080\"\u00F4d}\u008A \x04\u00D2;\u00A1~\u00E4}*V\u00A0{A\u00D2\x16\u0092\f\u00E2R\u00E3\u00C9\u00F6\u00B6\u00B5_\u00E9\u00A2T\x1F\u00B5P\u0080\u00F2\u00B2\u0088\u00F9\u0089\u00C9\t5\x1C\x00\u00A3\u0080\u00DD.\x18O\u00DC\u00FFg\u00EAI2\u0084\x0F\u00D8;\u00FF)\u00FC\u0089\x1F\x01<c\u00D9\x0B\u008A+0\u00E4\u0094\u00C7\u0090l\u00A2\u0095\x05\u00E3#\u00AF2\u00EE+\u00AC\u00EF\u00B5\x13\u00E2o\x11,\u00EF\u00D7\u008A\u00B7\u00BA\u00D4\u0084\x16\u0099\u00B8\"\u0083\u00B3\u008A\u00A0>K%9\u00AB<\u00FA\u00DD\x1B\u0093i\u00EA\x0BP\x0BU\u00FD[\x18M\u00FD-\x17\u00E4+nx\x02\x03\u008F\x11Pzo\u00E1]Y\x18\u0085\x06V\u00A5\u00D2z\"\u00AA\u00E8S_\u00F8\x11X\x14=\u008B-\x07-U\x00\u00BD\u00E1h\x01C\x1EL\u0081D\u00E73\u00A4\u00F9\u0090\u008B\u0091\u00D27\u00A4\u00BFC1\f\u00EE\u0091\u008F\u0091u\x10BH\u00E1\u00D2\u00DA\u008F$\u00A7r\u0083&AK\u00BD`;)\u00AD\u00D5gt4\u0092\u0082\b\u00B2\u00DAo\u00F4r\u00A0\u00D3\x04\u00C7R\u008C\u00C2\u0093a \u00CB\u00C3[ \u0088Ts\u00880s\u009F\u00C3T\u00A4\u00C2\u009B\u00C0D\u00A5\u00A8\u009F\u00D0`F\x0E\x18$\x1C\u00D9\x03\x1C\x151Y\u00CC\u00DF\\\u009CZD\u0086F\u00D4'\x06\u0092\u00D1gg\u00E6\u00B3J\u00EA\\\x07\u00A9z\u00B4k2UY\u0084\u00DF\u00DAj\u00C6\u00C1X\u00BB=\u00A1K\x13&f\b\x1E\x13\ni\u00B4\u0091\u008Cd\u00E0\u0095\u00E7\u009E\u00FE\u00A7z\f\u00C9\x10\u00BE\u00FF\u00DE\u009F\u00FE\u00B5'\x1F\u00FD\"^\u00BB\u00F7\b\u009D\u00D0\u008B\u00FB!\u00A1\u00AE\u00A1\x13h/\x03\x06,\u00C4z\u00B2-\u00DC\u0082<\u008A\u00CE\x13\u008C\x17\u00B19\u0090Qg@#jMB\x1C\x1B\u00D96\u00D6\u00B6=\u0098\u0089'\u00A7\u0096\u00CD\u00A0\u00A3\x07\u00DDCVlF\u00EE\x199\u00BE\n\u00BB\u00C8\u00D8\u00E3\x00\u0088\u0086BPD\u00DF\x1E\u009A\u00E1U\u0084\u00FD\u00E0\u00CA\u00C3\u00D2;\x19y\u00B8A5\f*\bIy\n\u0086\u00B2\u00E6\u00870\u00A1\u00B4\u00D6:\u00A5\u00D5_H \u0098BA|\x07R\u00FF\u0097\u00B4\x06%\u00BB\"\x01\u00A3b\u0092W`\u00D2$\x13\u00A0%}6bN\u0095\u00D9\u00CB\u00FD\x18\u00A8\t\x19\u00B5(\x04\u00A2\u00E5\bGl\u00DE\n\u00D1d\x10\u00BAT\x1B\u00A5_\u0097A3\u00C6\u009Cq\x13<oU9$\u00E5x\u00FF\\\u00D8m|I\u00CC\ri\u00E4w1\u00D9 \"\u00CFMV\u009A\u00AA\u00C0|n[,\u00FB\u00FA\fx\u00E53\x18\u00CE\u00B2n\u0092Q\u00E8S\tR\u00B3\u00FA\u0098\u009D\x173\u0090Z\\\u00CF\u00AA\u00CC\u00B6\u00DAcq\u00B2?RA\u00F4\u00BB\u00D6\u00FC\u00C15\u00D1\u00A2\f5\u00D6Y\x04n\x19\u00D6\u00CC\u00F5\u0094\u00EA\u0088\u00A1\u00B2(c\u00E2\u009D\u008B\u00EDf8\u009E|\u00FE\u00BD\u00BF\u00C6\x1B\x16C\u00F8\u0088\u00BD\u00E5W?8\u00DE\u00E5\u009F\u00B0\x17\u00AC\u0092\u0099Dl\u00E5ehql(\u0086p&v]\u00BB\u00DA \x12\u00B2K:\u00B7\u00DA\u008C\u008B\u00AB\u0092ha\u00CD|l\f\u00C1\u00CE\x06\u00C4Vl\u00846\n[\u0098\u0080P\f h'\u00A4\x11\u0084\u00C0\u0087\x10YWXb\"\f\u00EDo2\u00C5\u00B6H.\x18\u00F8:\u0080k\f\\Y\x18\x19eS\u00B1Y\u00FDH\u0094\u00C5o\x1B\u0098\u00AA\u00EC\x01\u00B5'J\u00D8\f1E\u00E5\x19\u00A0\b\u00B6\u00DB\u00A4\u0084(\f\u0096\u00B1\b\u0092\u00DC\x17\x0Fc\u00DE\u00F0b\x0E! \u009D\u00E7[&*eD#\u0085\u00E0D\u0085%\x1B\x17\u00F0>\u0085\x1A,\u009F<fx\x10\u0084\x00D\u00EC\\kdZ\u00B9\x06oy(R5\u00E9j\u00B5\u00D4\u00FE4@\u00B6\u00DF\u0092\u00F0\u00C5\u00E8\u00FB\u00B1\u00F8\u00D4\u00E6\u00A9\u00C6uQ\u00D2\x7FV\u00870\x11!\u00CB\x1B-\u00FC\u0095_\x00\u00BF\u00C6\u00A2.t}&p[\u00DD;\x13\u009B\u00B8\u00862h\u00A8\u00A1\x01G\u00B5\u00DF\u0098VV\u008D\u00EA\x12\u00BC\u0095\u0083\u00CB\x01\u00A7\u0084\u00E0\x18\u00D6\u0081c\u008D\u009C\u00A4\x1A\x012\u00AB\u00DEN\u00D2a\u00ACu#3{\u00FC\u00F6k\x7F\u00FA\u00C7\u00FF\u00F2\u00AF\u00E2\x7F\u008FV\u0093!\u00BC\u00CF\u00ECK?\u00F6\u00FA\u00DF\u00FF\u00CC'\u00F0\u00E2\x07ej+H\u00CD`\u009F;\tL\u00D3\u00AF:\t\"^\u00C5y\u00EBw\u00BEO^\u008B\u0085q\u00E4\u00EF\u00FD}\x0E\u008B\u0096\u00DD\x00K\x1BI\u00E4\u00B9+\u00D2\u00AA\u009A\u0094\u00FF\u00B5\u00A3\x15qo\u00E3\u00FFb\x16=\x1A\u0092D\u00BC\u00A8Qu\u00DF\t\u00C3C\x07^\u00B7\r\u00D7pl\x1E\u00EE\u00A0a\u00A1>l\x1E6\x02\u00DE\"\u00D9B\x1A-\u00D3&\u0090\b\u00B7\u00D4\u0082F\u00E4\"0\u00C5\x14\u00A4t\u00A7Tw\x0F\x02\u00DFR\x1A\u00C7=\u00BA\u00EBR\u00A877Q:\x1AA\u00F2S\u00C6\u00BF\u00AA\x1F\u00C4>\u008B05\x16\u00E7\u00BD:\"H)Y}\u00D6\u00BDG\u00BB\x0F\u009C*\u00D5\\\u008F\u00F5\u009D\u0098\u00D3\u008E#U\u00A71\u00C1\u00BE\u00A3t\x11\x14\u008A\u00B97\u00D5\u00C3\x16b\x01\u0096*\u00C4\u00A9\x16\u00E8\x1C\u00BD%}\x19\u00AE\u009F\u00FAz\u00D3\u00FDS\u009A\u00AB\u00B8js\u00EF\u00E1\u0092\u00F7\u00EF\x10]\u00F5!Cu\u00DB`\u00CE\u00CDl\u00BB\x0BuQA4\x0E\x11\u00B9\u0090\u0081~\x13\u00C7\u00F4\u00EC\u00C3:6\u00A9\x1B\u00CD~!\u00E6#Zy\u00FB[?c\x1Fy\u00CB\u0097D)\u00C9\x10\x00\u00E0\u00BD\u00FE\u00F6_2\u00B7\x0F\u008Ah3\u008A\u00CFD\b@\u00A1\x07 \u00FD\u00F4\u00A0\u00CF\u0099\x06\u00C9\u0099\u0093`yM\u00955\x0B\u00FEtk\x0F\u0087[\x05L\u008A \u0097\u00D0\u00E3\x055D{\u00B3\u009D\u0087\u0093j\u00D0c\x0B:\u00C3pI\u0096\u00AC\x16\u00B2\u0096+\x13*\u00E8\u00C6\u00C7\u008A\u0094\u00F4\u00E5\u00FE\u0087\x01\u00AF\u00B8\u00E3\u00DA\f\u00BB\x19v\x0F\u00C6p\x03$:\x00h\u00AD\u00EFk\x7Fz\u00956s\x12\u00E2\u008C~\u00DA\x01\u00AA\f\"\x18K\u00AF\u00C2\u00A1.O\u00A1\u00DFF\u00B0d\x12\x10s\u00F0 \u00A8\u0083D\u0099\x16}\u009E\u00DFK\u00B0\u00FBD\u00C4\x00\u00E8\u00E5\u00C0\u00B0\u00C6\u0090<l\u00E1rM\u00CEYL+\u0097dcf@yBrW\u00B1\u00C6\u00D8\u00D4\u00D7286\x15A\u00CC\"\u009F\x1BNo1\u00D4\u00FA-\u00E3+d\u00A3\u00E8\u00E7\u0083*\u00E1\u00E4:q\x12:\u00AA\u00AD\u008A\u00AF\u00D9B\u0082J\u00D7q\x0B5,\u0083\u00DB\u00C44\u00E4.\u00D4+\b\u00CE'\u00C5\x0F\u00ED\x01\f\\\u0084\b\u00D1\u00DD\u0083V\u00BC%9e\x0E\u00C3\u0085\u00B5\x15\u00A4\u00E7\u008Bx\u00D5H#\u00F4\u009C\u00F5\u0081^\u00DD\u00A9\x06m\u00C5h$\u009CY/\u00CF\u00E0p\x03.\u00CF=\u00F5K\u00F8T=\u00F2\u0085!|\u009F}\u00C7/\u00DE\u00BB\u00F9\u0095\u00FF\u00E1\u00D1\u0095\u00A3\u0082\u0091\u00F8@nA\u00EE\u0095p\u009D\u00C5V\u00ED\u00D6o\u00ED\u00A1v\u0082KH\u00DE8_2\u009E&\u0099\u00FB\u00F9\u00F9\u00E2\u00C4*A\u00A5\u00D5:\x18\u00D4!\u00D7\u00ED\u00CD8\u00C1\u0089\"z\u00BF\u00C6r}\u00D9(\u00AA\u00DF\u00B1\u009EVd\u0091\u00F5\x13\u00B9j_7\u00E05\x18\u00EEa`\u0087\u00A5;\x11*)\u0097\u00FD\x103X\u00AD\u00EF!\u00C1\u00ED\x14{\x10v\u0084\u0084\u00E3(\u00E8\u009F\u00926\x19\u0081S\u0087\u00AFLHy\x16\x04\u00E5\u00C3\u00F2\u00EFI0\u009D(\u0085\x0Ez^\u00C1\u0092F\r \u00CAzX\u00D9\u00BF\u00BCl\u00EC\u00AA\u00C3\x004\x1E\u008B\"\u00F8\u00AE\u00CEt\u00C4\u00D0]\u00E4\u0098\x1E\u00DC\u00B5\u00F5\u00A1\u008F\u00D5[\x1B\u0089\x0E\u00A4\u0097\u00CF:&\x06\\\u008C\u0083\t{\x0E\u00944\u00E6\x059wh\u0086G\u00E7\u00F9#\u00CE\u00CB\u00F5\u00A2\u008E4\"\u00CE\u00AD\u00E8[\u00DB\u008B\u00D1\u008E\x1C\u00D0\x07\u00A0\u008D\\\x12>\x01\x05\u00BB\u00B8\u00BEr\\\u00FCM\u00ED\u00E6\u00EF\u00CD\u00CE\u0090\u00A8\u00C2\u00EA\x1Egd\u00A1\u00F3'\u00DA\u0084\u0087 \x7F\u00BC\x01\u00F6\u00FEw\u00FFb#\u0086\u0095!|\u00F4\u00FE\u00BB~\u00E1}\u00AF\u00BF\x03\u00BF}\u00F5\u00E5vt\u0095\u00C6\u009E\u00C7:\u00DC\x1F\u00C8-\u00C7\u00FC\u00FC;\u00FFn\u00C4\x1F>\u00DD \u00CC,n\u0092\u00CC\u00E0\u008D\x18\u00C2\u008A\x1ATqF\u00D7\u00A4m\u00C3\n\u00AA\u00A9\n\u00D1r\u00AD\u00B7<\u0085\u00B4\x17l\u00B7\u00EE\x1B\u0083\x1D\u008D\x19l\u00CB\u00FD\u00FB\u00F6\u00EF\u00B1L\f\u00DFp\u00E0\x1Evlp\u00EC\x18\u00D8<\u0082\u008D.\x1E\u00E5\u00CC\u00A4\x16\x04\u0093\u008E\u00E4\"-M\u00D5\x18H\x15\x01Z\u00ABA\u00D4\x07\x17\u00D0\u00D1\u00A4\u00A2\b9\u00D2\u009A\u00D9e\x14\u0081\u00E4\u008ELd\x00Z\u0087C\u00D2\u00B7\u00A1\n\x1D\u00D3\u0086\u00AFB$\u00C3%\u0093\u00C2\u00FBdIG\u00B1\u008F\u00C2\u0086-=\x1E:\u00B7\u00EE\u00CB>u\u00D5g\x16\"\u00A8\u00BC\b>=\x07\"\u008B\u008F\ts\x0B\u00E1\u00AB\u00CF'\u00A2!1;\x12_\u00AC\u00AF<O\u00CFT\u008C\u00E1\u00A8\u00CF)\u0089?Q;:\u00B5\u00CE\u00FB\u00A5\u00D94\u00F8w\x12\u00B4\u0088\x10+\u00A3\x01\u00EF\u0099\u00F0\u009D\u008Cb\nRq2\u00BA1S\b\u00C3\u00B5\x07EW\x11f1\"\u00B9%\u0093qh\u00CD{1\u00B1T\u00EB/\u00A8jQU\x0F\u00C1|\u00E0\u00F2\u00F4\u008Eg\u00FF\u00AB\x1F\u00FC\x05\u00FC\u009F5]\x0BC\u00F8n\u00BB~\u00E1G\x1E\u00FE\u00F4g\u00FE%^~\u00BE\u00D7\x02\u00C0\u00F2\x1Dy\u00B3\u0090\u00EA\u008D\u0098\u0092\u00936I\u00DC\u00F6?\u00E8j\u0082\u00BC\x0B\u0095r\u00DC\x19\f\u009A\u009A\u00D0\u00B9\u00F3\u0086\u00D9\u00C3\u009D\u00A5\u00A64\u0086\u00E1\u00E9\u00D6T@\u0087\f\u008E\u00C0Rm)\u0099A\u00BF?\u00ED \u00C9TP\b\u00A6\u00A3\u0083\u00A5_\x15\u00DCt\u00B1\u0081\u00AF\u00C33\x16\u00C1\f,f\u00CA\x18\x03\x00GZ\u00DA-\u0098\x02\u00AF\u00EF\u00D0^~\u00FF1[P\x10\u00AF\x1F\u00D3\x1B\u0081\u0095\x0BQQ\u0081Z\u0093\u00DDX\x18\u00C2\u00C4)\u0095-\u00897\u0089qz\"\u008F\u00E1\u0096\u00CCE\u00C2-\x10\u0080\u0095\u0080rP\u00ED\x1A\u008B\u00E4\u00EFu\x1A\u00A09\u00C8\u00BFcN7\u00ADc/\u0084\u00B2%\u00BA\u0089\u00B5\u00B5\u00A0\u0080\u00E6r\u00ACc\u00D5\u00DEtz\x04H\u00A0v\u00CB\x18\x19\u0081tB+\u0091\u00F4E\u00CC\u00E6\u00DA\u00EBA\u0091\u0084\u0094\u00BC\u00F3dO\u00A0\u00A2\u0097\tV\x18pS\u00A1\x19I`\u00C6Kx\u00EC\u00BF\u00AD\n\u00CA\u009AD\u00F3\u0083\u0091\u0092\u00E5F\u008C\u00BC\u00A8\u009Dm4\u00FA\u00E9\u008C\x105VU\u00D3\u008Cc\u00EA\x1F\u00DAuz\u00F8\u00FA\u00EE\u0088\u00BD-\u0095\u00D8\u00C4\u00B9\u0085a\u009A\x03\u00DF\u00FE\u00F6\u00CF\u00D8\u008F\u00BC\u00FF\x05\u00B4\u00D7\u00C2\x10\x00\u00E0{\u00E6;~\u00F6gl</\u00E2\u008C.u\u00B8>8\u0091\u009D\x10[\u00A7:\u00F1t(\u009E\u00E9\u00CD\u00BD\u00AD\u008E\"\u00B6\u00D3\u00F1\u00DB\u00EF\u00DB\u009B\u00C4\u00C4\u00F7\u00E1\u0083s\u00C6\u00A4%\u00FD\u00A6\u00CC/-\u00A6\x1E1\u0099{ET?\u00D3^\u0080vn2\u0081\r\u0089.\u00AC\u0098\u00CD:f\u00C7C\x18\u00BEj\x03\u00FB\x04\u00EE9\x0B\u00A5R\u008A\u00B1FN\x12\u00F4\u00A4J\u00A0\u00C8>\u00C0\u00D2((&!\u00D5\u00C2\u0088\u00DD\u008F\x19.\u00D2M\u00FA\u00BF\x0Bq`\u00B1!dfb'tIq\u00AE\u00A5(\u0092\u00EA\u00CC\x1Dp8\u0089o\u00CCbJ{5Q\x11\u008D a\u00B1\u008Fi\u008Bh\u00FD\u00D7\u00A2\u00B6\u00F6]R\u00DE\t\u00D5\x1Dq\u009F\u0091q5\u0085|\u00DC\x11a\u00FAn,\x04\u0082$t\u009F\u0091\u00BE\u00ED\u00D2\u00ED3\x14:\u00A4q\x06Z\u00E5\u00B9\u0096L\u00B1\u0092\u00AB(\u00A4\u00BA\u0084NC]\t\u0084 rI\u00EA\u00BBb\x06&m#\u009D@\u00DBy\u00B7tz\u00D4y\u00ECo\u00ACC]\u0083Z\u00AF\u00F9\u00BD\u00DFW*\u00C2D\u00EE\u00A9\u00B0\u00C4\"\u00F4s\r\u00E6\x17T\u00887\u00F2\x1E\u00E6\x1B\u008Eo{\u00FAgqz\u008D\u00F3\u0081\u00FF\u00E0\u00FEw\u00FD\u00C3g\x1F\u00DD\u00C7\u0098w\u00D4QDd&\u009A]a\u00D8\x0E\x187iI\u00FD\u00BC%7\u0099\u00D0C\u0087\u00E3\"\u00C8\u00E4\x1B\u00B5A\x00\x00 \x00IDAT*\u00E6\x1A.\u00C2\u00BD\u00A1\x07\x15\r\u00EBz}\u00B4\u00E3\u00CB\u00DF\u00CDGl\x1CJ\u00BAU\u00E4\u00FA\u00B3\u00E6\u00A9!\u00E13|9\u008C\u00A0[\"\x1C\u00A91\u00E9eX\u00DE,^\u00D2B\u009F\u0085z\u00AA\u00F2\u0093\u008C\u0097\x03\u00AF{\x04*\x1Dn\u00B8x\u00F8\u00FB\u00C3F\u00E0\u008B'\u00A1\u00DE\u008E\u00C9E\u00ADu\")/f//\u00C1\u009C\u0091[p\u00F0\u009A9\u0083\u0090\u00E7\u00E9\u00AD\u00B5\u00AAuz\u00F0^B\u00C8\x19\u00DF3\x19\u00EF0\u0091\x04\x16*J]w\u00CC~\u008DS\u00F5\u0089\u00C0\u00A7c:n\u00DC\u0097\x02IP\x7FI\x0E\x1D!\u00EB\u00DA\u00E9V(\u00A9\u00CD\u0083\u00CF@I*\u00FD\u00BE@b\u00D7*%\u0093\u00F6\n\u009A\x1A@\u0096\u0083[\u00DD\u009B\u0085\x18neZfM\u00C7\r\u00F0\x1D\u0098;07\u00F8l\u00C7\u00F2S\x1B!\u00C7\u00DF\u00A6\u00E8A\u00EC\u00A8\u00AD\u00D8u\u00DEu\u009E\x17\u00EE\u00CD\u00ABjK\x05\u0088\u00F4\u00EE\u00D7\u00EB\x1A\u00EDk\u00BA\u00D0^[\u00FB\u00DA}\u00BA\u00F5\u00CF\u00FB\u00F5\u00DE\u00CE\u00CF\u00FB_5Z\x1C\u00B8l\x03\u00E3\u00FB\u00DF\u00F7\x0F\u00CF\u00F4\x7F\x0B!\u00FCy{\u00F6\u00E7\u00FE]\u00BC\u00E7\u00D1\u00AF\u00DA\u00EF\u00DE\u00EB\u00B0\u00B8\u0088zG\x16\u00FAL\x02\x04\x1F\u008B\u00A1P\u0085\u00A44P\u00D27\u00AEY\u00C2\u0099\u00F9\u00C0+\u00D9H\u009F\u00ED\u00E1;\u00D1\u00C1\u00B2\x07\u00A4\u00E5\u00B92\x1C\u00F6\u00C0\u00A9RE\u00D8Fk?@b\x0F\u00A1V7\u00FA\u00C4\x03\u00C5\\\u00BA\u00BA\u00A4>j\f\u008E\u00B6$\u0089\x13\x1C\x17g\x1E\u0085#\u00BD\tA\u00DC\u00D4yi\x1B\u0088JJ\n>\u00F2B\x04\u0094\u0094\u0087\x16\u00F8\u008C\b?\u009Fq\u0097\u00CE$J\u00AA:#\x15u\x1FKB\u00CC\x1C\x05\u009D?\u00CB \u00A9\u00DC\x07\u0097D%\x172X\u00AA1Q\u0088K^\x10!\x02\u00B2E\u00A7\u00AD]\u0089Y3f\x19^\x06\u00C5\u0090\u00B4\u00D1\t\u00C1\u00F7\u00C12\u00EE\u00E9\x01\u00A14W\u0094\u00A3\x10s\"\u009DE\x05\u00AA\u00EFe\u0098;3\u008E\u00F6J&P\u00DF\u00EB\u0089\u00EA:\u00A3T\u00D5X\x1CY*=\u00DB\x10\u00F4\u00E8\u00A1\u00C4\n\x18\x02T\u00D2,\u008D\u0094\u00D3\u00E1\u00C9@\u0094:}\u00E4*\u00F2D%\u00CCjT?:\u00A2\u0080\u00A4\u00BF\x10E\x1B\u00A3;j\u00C3]\u00F6\x07\u00EC'\u008D\u008F\u00F2x\u0098\u00E0\u009A9n\u00DE\u00FB\u00F4\u00A3\u00A7\u00FE\u00BB\u00BF\u00F0s\u00F8\u0089u\u009An1\u0084\u00E7l\x7F\u00E5\u00BF|\u00F8s\u00BF\u00E86~\u00B8\u008E\x0E\u009E*C\u00C5\u00A8\x0E\u00A5\u00CAP\u00C5RB\u00B7\u00E7r\u00D1\u0080\u0086\u008C~\u00DDx\u00A7c\x01\u00BFWVP\u00F5\x0B\u009C\u00C3Z`?V\x02\u00F7$\u00EE\x16\u00FF\u0090\u00C8@j\u0081\u00AE\u00DB\u00E0]e\u00C8qX\u009B\x12\u00E3\u00BF\u00C5\u00F4\x16F\u00D7\x18agnN\x11yxHP\x11\u00BD\x13*O!\x06\x1A\x15\u00A7\x1E\x19\u00AF\u00D5nJ\u00C3\x11\u00D5\u0090\u00C8\x14@\u00B5A\x19\u0086\u008E\u00D2\u00FB\x15\u008E\u009C;(u&\u00D1\u0088\u00DD\u0080\u00DC\u00D9I\x11\u008D8\u00BC\u00DC\u009E-\u00F3\u00D1!b\u00B6\x1Ce\u00E4^h\u00B1\u00B3\r\x0B\u00C2\x0E\x1E\x12\u008B0\u0092\u0098|\u0089\u00A3\b&`\x00fTnj\u00AE\u00C1n\u00EB0\u00F6\u00CFip\u00D5\x18\x143\u00A1\x07\x11\f\u00816\u008B\u0084\u00DE\u00FC\u009C\u00F5\u00B7\u00C3\x19\u00E2\u008Cb*qi2\u009Ae\u00D3\u0099\u00CC\u00C2\u00CD\u0089F\u0096Ws\u008B\u008E\u00E4\u00C6\u00AB\u0084\u00ED\u00D3\u00B1Z\u00F8y.K\u00B1+\u00DE!\u0093\u00962\u00B0J6\x01^\u00DF\u00C2\u00AD\u0093\u00DB\u00FB\x01\u00CCKk\u00B7\u00F5#!%\x10\x1B\bKl\u00B5\u00E8Z8*J\u00F1\n\u00C0\u0084\u009B\u00E3x\u00F7\x13\u00BFh\u00FF\u009E\u00BD\u0082\u00D3\u00EB\x16C\x00\u0080\u00BF\u00E0\u00DF\u00F5\x0F>v\u00F9\u00FF~\u00F8f\u00D7\u00CD\u00A5\u00FF\x17\u00A1\u00E8e\u00F2\u00DF\u00B3F`\x11\x19\u0089\u00C4\u0080\u008A18C 0\u0083\u00F2.\u00BB\x02=\t\u008B~\u00DF\u0091\n+\x17\u00C7\u00D2E7\x1E\u00CAJ^5\x1D\u00A4N\u00C4\u00C4\u00DD2&.\u0086I\u00B4\u00F6;\u00E3\x11S\x18\u00CB1Y\u00B6\u0095\u00C9\u00E7,2\u00F8h\x1An\x1ATO\u00D8\u00EC\u0096\x06F\x00\u00CCh\f5@\u0086\u00C0L0\x02\x16\x03\u009A\b\u00C4dg\u0090\u00AE\u00CF\u00B5\u0093\u00C4\u00E4\x15\u00B9\u00E8It\u008C\x18\u0099u\x1D\u00846\u00DA\u00B1@3\u00B1\u00FD\u00DB!\x15\u008A\u008B.\ri\u009C\u0081m\x063\u00BF\u00C0\u00B1\x13\u008EOg\u00D19\x0Fu \u00D9*\u0089=\x19\x1B<m#\x19k!\u0083\u00AB\u008C\u00A5\x1A/\u00AF\u00DD\u009A\u008D!dh\x10\u00BD\u00F2lJ\x1D\u0090@\u00A0\u00B4\u00EFK+\x16\r2\u00D8\t\u0086\u00B21\x00UC\u0080s?\u0085\x16J\u00F0E\x04\u00E9\x01LUS\u008E\u0095\u0091\bb\x1A,a}/\u0088\u0082\u00C6H\u00FA\u00C2h5\rP\u00CD\u0095\u00ADA\u008B\u00FA\u00A8\u00BE$s\u00D2\u00B9^c'zq\x16e\r\u00C0\x11\u0093f>p\x19\u00C0\u00CDw\u00BE\u00FD\x1F\u00E0\u009F\u00E0\u00D6\u00EB\u0096\r\x01\x00\u00FE\u0093\u00FB\u00EF\u00FD\u00D8\x07\u00E6;|\u00CCU\u008F\u00AE\b>\x12ZfE\u008E;\u00DE\"\u00C0~\u00AB\u00B3M\u00A1\b\u00AA\u00AE\u00DB\x11[\u00D2K\u00AF:\x13&JE\u00A0\u00B1/\f~\u00E2\u00EC\u00D4\u00B1ny\x10\x06\u00A2tv\u00BB\u00FF9\u00FE u\u00C4\u00DB\u009E\u0084*\u00F4\u00D2\u00DA\u00CB{\u00C6;\u009F\u00E74\\(\u00D9\u00F5\u00CC\u00F3;\u00A5c\u00EA\u00CF\u0093L\u00C1\u0095Oa\u00A9\u00EE$\u00E4\u00EF\u00C2Az\u00BD\x17\u00C1\u00CF\u00A3\u00E9\u00E6\u00A9\u00DB\u00CB\u00E6@\u009B\x02\x17N!\u0096B\x1F\u0093\u00E7\x1FG\u00BC'\u00EB#\u00CAF0\u00B3-\u00ABX\u0089n\x0F\u0098#\u008D\u009D\u00C1\u008C\u00C8\u0090P\u00B6\u0082 \u00A2\u00F8\u009C\x138\x0Ek}\u00D4\u00FCx\u00DA=\x0E\tH\u00BD\x1D\u00CB8\u009C\u00C6\tsh\u0093\u008C|{\x12\x1B\u00EEx'w\x03\u00AB\u00E1\u009E\u00D4\x11\u00CB\x18\u0093e-S\u008D\u0090g\u00A7J\u00B9\u00C5zr\u00A1S\u00DFB\u00D27;\u0081\u00B9\u00EC\x0E\u00D7\u0080_\x03\u00F3:\u00EC\x15\u008B\u00DE\u00AF\u00B2\u00EE\u00ED\u009D\u00F7\x1E\u00ED<}\u00B2\x1D\u00D9\x07\u00E65l\u00B6\u00F6}\x0F\u00BA\u0090\u00DD\x0F;l\u00EEp\f\u00DC\u00BC\u00E5\u00CA\u009F\u00F9o>\u00FC\u00B1\u00BBh\u00FFN\u0084\u00F0A\u00DB~\u00F7\u00BF~\u00F8K\x1F\u00FFW\u00FE\u00D5\u008F\u00CC\u00A5nA\x10\u00AEe\x0EB\u00B9%m!\u00B23\f/+\u00FDZ\u00DB\u00E0,\u00F5\x19l\u0094\x04\u00DC\u00EB&\u00A2\u00A1\x00\u009D\x1B&HG\x11\x11 \u0097\u00A6\u00FA\u008D\u00ECWff\u00BA\x173XB\u00A9\u00FB8\u00D1\u00EE\u00D3\x19K\u00EF;\u009A\x0BO\x12&\u00A4\u00CD\r3\x07':J\u00B0\u008A\u00CEu\u00E0\u009C\u00C9\x18Q\u0088\u0094\u00F2\u00D0o\x11\u0085\u0098\u0082%\u00D1E;N\u00E2\u00A9\x00$\u00CF\u00F6A\u00C4\u0090:\u00F3\u008C\bFg\x00P\u00EE\u00FE\u00EC\u008C\u00E4\u00F3\u00AA\u00BB\x10\u00B6\x03/\u00CF\x01\u00CA\u00D87\u00E0\u0090{V\u0099\u0091@\u00FB\x1B\u00CA\u00B3@Ju\x17\u0082\u0090\u00CA\u00D2\u00E6m\u00B4\u00B9\x03\u00D1VO\u00BA\u0082\u00AB&\u00A4\u00B7\x18\x05e?\u00A2\u00A9\x1D\u00D1`\x0F\u00DB\u00C6\u00F2\u00DB\u00E9\u00D8<}w\u00F0\u0081\u0090\u00C1\x1B#\x0F}T\u00C9x\u00D9\r\x14\u00A3 \u00AEE\u008B\u00BFv\u00A7\x06c*\u00EAw\u00D9\x11\u00C8\u00B4R\u00FA\u00CB\u00BE\u00E2\u0088HJM\x16\u00E0SnI\u00B95\u00E3\u00D9G\u00ACB<\u00D0r[:J\r\t\u00E1\x1A\u00DB\u00BC\u00D5\u00F6\u00F06\x07\u00E6\u009Fz\u00FA\u00E3W\x7F\u00E5\u00B9\u00DF\u00C5\x1D\u00AF;\x19\x02\x00\u00FC\x00\u00DE\u00F9S\x1F\u00B3\x7F\u00FD\u0091\u00AAfUDy[\u00C2\x07\u0087\u00B4\u00D4\u00CBA\u0082\x04:\u0087\x1DY\x7FQ\u00EDm\u00D9\u00AE\u009F\u0099\u00C1be\rh;{{I\u00DC@VC\u0082\x01v\u00F2\u008E\x18\x00\u00A2\b=\u00E0\u00DB\u00DB\u00C1\u00F7\u00B2pg\x15\x01\u00D5Vg^\r\u00A2NJ*\u0083\u00D5\u00A2\x02p\x03\u00C7\r\u0089T\u0096{\u0085/\u008B\t\u008CR7\u00C38\u00D7\bG>{\x05\u00FA\fG\x06\x0B\x01\u00F1\u00DB\u0085\u0084\u00B4\u00CFb*N5\u00C3\u00F8\u0099q\x0B@\u00EA\u00F5\x1A\u009C\u0098\u0098\u00F4v\x19\x1F\u00BB\u00F1o\u00BAs\u0099\u00D5\u0098\u00C3U\u00CA4m\u00AF1\u009B\u00CF\u00A62 \u00F5~#\u00C3\x19\u008C\x19\x18d\f\x19\u00FC$\u00E64G\u00AA\n@\u00AF\u00CD\u00C0\x152\u0095m\u008A\u00AA\u00CE\u00D4\b\u00BE\x04\u0083\x06\u00D9\u00C6\u00AAx\u0085\u00C6\u00BC\u00CE\u00E7d\u00ECB\u00DA'\u0082).\u00A1\u00D2\x13\u008D\u00B9lM\u00AD\u00F0\x12(\u00F9}\u00AE7\u00E8\u00AEE\u00D9 \u00A4\u00DFg\u00CE\x01 [\u0081)\x122C\u0093\x072E[}N\u009BF\u00CF]\x10zi\u00CC\u00C2\x01\u00C7\u00C4\u00FCw\u009E\u00FA)\u00FC:\u00EE|\u00DD\u00A92\x00\u00C0\u008F\u00DE\u00FB\u00E0O}\u00F0\u00E6-\u00BE\x10\u0081`\u00B3 \u00CCbl$l61\x03\x11<\u00DF\x19\u00C0\u00D4a7\u00BB\u00E0;\u0099A\x18\x11\u00ED\u00D69\u00E7p\u00E9jG\u008B#N;K\u00F2\u00DEO;\u00FD^\u00FD\u00B1\u00E5\x1A\u00AC}\u00EB\u00EA\u0092\u00EE+#\x1E%SE\u00F9\u00A1`\u00EB\x04\x1EM\u00C7\r@\x17\u00A1\u0098\u0080K\u00FDL{\u00D4\u00CC\u009C\x16K\x1B\u0083O\u00C3<,az\u00BEy\u00CC\u008F\u0080\u00F5B\x1D\u00F9NhO(~4\u00F5\u00E1\u00A0\n\u0090\u00AA\u0081G{\x07\u00DD\u009ErW\u00EAw^{L\u00F0\u00BE\u00C5\u00A0\x0E\u00DA\x1Az\u00DF|:\u00D5\u0085~\u00CC\u00B8e\u0081T\u008F\u00F35\u00D5\u00EF\u00EE\x02\u009D\u00D3\u00DB=J]\u0099\u00AE~G\u00BFr\u00D76\u0097:ABey\u00B8\u0088\u00AD\u00A8\u00DFJ\r\u0093\x04\u00D6\x066\u00BC\u0096\u00EB\u00C9\u00D4Tz]\u0096\u0085F\u00C6P\u00AA\u00A6\u00DDrU\u0096*\u00E0R\t\u00F2-\u0098\u00AF\u00BF\u00EFP\u00B9\u00FD\u00B4\u0096{\u00DBrUN\u00B9\x14\u00AF\x01\u00BF\u00C7\u00F75\x7F\u0097\u009BQ;\u00A0E\u00B1\u00A0\u00D7\u00DF\u00BE\u00F93\u00FF\u00C5\u009F\u00FD\u00A97 \u00FB7F\b\x7F\u00DA\u00EC\u0085\x1F\x7F\u00FDW~\u00F9_\u00E0\u0095\u008F\u00C2YJ\u00AAy\nn\u0085\x14\u00B7\u00FA\u0081\u00E7\u00A2$\u008A:\\#\x1D\x01\u00E9\u00EB\u00B3\u00B9\x0B\u00D3H\t\u00B9\x07\u0085 4I\u008Ct\u0084\u009E\u009D5\u00A9/]\x0B\u00AD\x0F@\x1A\x18\u00D1\u008E\u0091)\u00DC\u00AE\u00C3\u00B8A\u008F>$\u00A4T\x0B.\x07.\x0EA\u00D9\n\rnp\u0094R\u00F6r8\x0Es\u00CC9\u00B8\u00CFb\x15AI\u0084\u00C0\u00CB\u00F2;%|W#F;\u0096\x05Mx\u008D<\x12\x12|K*q\u00EB\u00A3\u00DC\u0097F\"\u00DCH\u0080\u0092z\u0087\u00EE\u00D5\u00AE-\u00A3=U\x01\u008D\x1F\u0092\u00F8H\u0097\u00A190\x19\u00DA,dQ\x11\u00C0U\x02\u00DE=\"\x13\u00D30*\u00F4\u0091\u00F7\x1F\x10\x021_\u00C7\u00BCTt\u0086\u00B3\u00DC\u00BDW(\u00F4\"\u00F5%\u00A9\u00DB|\b9,\u00A8\u00A2\u00A9*\u0089\u00F0|}\u00B62\u00C0\u00E6\u009C\u00B6\u00B61\u008A\u0091\u00A4\u00D1C\u00AE\u00C7\u0086\x0E\x16\u0086R\u00D0$\u009E\u0097\u00D0\u0085\u00AE\x15\u00E4?\u00A0\u00DCu\u00CF\u00FCu\u00F5O\u00E8ChH.M\u00A1W\"\x03\u00C5\x04\u00F9\u00C4\x1C\u00F7p\u00F3\u009DW\u00BFl\u00FF\u00ED;_\u00C0\x1B\u00BC\u00DE\u0090!\x00\u00C0G\u00ECO\u00FD\u00DD\u00BF\x7F\u00F3{\x1F}\u00BC]\x16\"\u00AF=\x12:q\u00F5c\u00C5\u00D9\u00AA\x0E\u00C0*\u00F5\u00B3\u00E8\u00E8\u00A9\\\u009B\u00B6\u00DD\u00AA\u0088\u00C0j#\u00A7\u00B1\x0F\u00B4\u00DF\u00FF.$\u0090v\u008B\u0095!\f\x17\u0091\u009FQAj[q\u00EF\u00BE/E.\x12\u00AC\x0BD\u00D0U\u00DFy\u00F8\u0082\u0081\u00C3A\x03c\x04\u00EF\x1C$\u00AC\u00E9U\u009D\u00C8QD==\x12j\x05SwJho\u00E7ed\u00A0W\u00E6\u00E3\u00A51\x0F\u00E88\u00FD\u00FAJ3\x16\u00C1\u00F7\u00A8FL\u00CE\u009A$hk[\x0B\u00FF\u00A0\u00E7@%\u00C2&G\u00E8\u008D\u00C8\u009D\u00E3rxf:b\x02\u009BE\u00F4\"\u00FA9\u00C9\u0080\x14\u00EBPm`\"c\x17\u00E4M\u00E8\u00CC\u00AD\u00CF\u00BDP\u0095\u00C6\t1D\u00AC\u00E3[?m\u00B5\x1B\u00DCq\u00CE\u00CA\u00ECQ\x04\u00B8x2\u00B8fRU\x00\u00B2&\x02\b\u00D1U\x1C\u008F\u00B1\x07\u0091'b\u00A8m\u00DF\u00FA\u00F5z\u00C8\u0082\u00FD:F\x18\u00E4\x07\u0096\u00ED\u00E0 \x15aba\x10d\x04A?\u00CC\u00A6\u00E4\u00E0|8\u00F0\u00DD\u00EF\u00F8\u00BB\u00F88\u00DE\u00F0\u00F5\u0086*\x03\x00\u00FC\u00A7\u00F7\u00DE\u00FB\u0093\x1F\u009E\u00EFx\x18\u00C4Qq\b\ti8 opz\u00F8\u0086\u00D1\u00AC\u00F4+\x1C\u0097\x0E\u00B4\u009F\u00DA\u00D3y\u0081\x16n\x1B\u00F1h\u0094LuE\u00CFF\u0088A\u00D1]c=\u0097\u0086\u00C9\u00DBpl\u0087\u009B\u00FA\u008D\u00EC\x03\u00D2h)\u00A4\u00D1l\x1Cm!\u00F4\u00A2\u009F\t==\u00D4\u0081\\\u00B03\u00DCv\u008F\x0E\u00C3\u008Dw\u00CF\u00C2\u00EA%\u0098h\x7F\x0B\u00E6;\u00D5\u0081YB'\u00ED\x10\u00B3\u009D\u00D7>/^P^P\u00DB\x1D8\u00E6\f\u0082\x16\u00C3Q{\u00BA\x7F\u00FB\u00EE\u00ED\u009E\u00A1J \u00A3\n\u00D3s\u00A0hH\u00AA\x15\x19U\u0098\u00D0\u009Fj\u008FGM\u00C9\u009B\u00D6\u00DF\u00F3\u00BB\u00E6\u00C4\u009AJ\u00C2m\u00D4\u00BDy7\u00BC\u00F7\u00B7\f\u00C8a\u00E0l\u00A5\u00E7\u00F8\u00EE\x04\u00EEd\u008Cg\u0086\u0082~\x1C\u00ED\u00B7\u00E4L\u00BE0)\u009D\u00B3\u00A8%\u00C9\x14\u00F8\u0096-`^#\u00EA\x1AF\u0084\u00A2\u00F9\x06\u00CC+\u00B8\u0087\u00F5\x7FQ\u00A53\u00E2\u00F1\u00A4\x0E\u00EBxF4n\u0080\u00BC\x14\u008A\u00AA<\u00AB\"\u00E9\u00B9 \u00CAV\u008A\x01\u00D5\u008C\u0087\u00DF\u00BE=|\u00F6\u00AF\x7F\u00EFO~3\u009A\u00FF\u00A6\f\u00E1\u00BB\u00CC\u00BE\u00F2!\x7F\u00EB\u00C7`{K\"\u00A2\u00F1P\u0095\u00833\b(\f\x19\u00D3\x00U,\u00AAP\u00A3u\u00A0\u00B2H\x1BTL\u00B5\u00D0\u00831\u00F6 \x02\u0087\u00C2]\x02l\u0084\u00AD\u00BC\u00EF8\u00D9#\\L\u0083;K\u00A5j\u00D1\u0099\x00\u00F2\u00F7^\u00E8%\u00D3\u00A5q\u00DB\u00FB\u0091\x15j\u00B5\u00C8\u00A6g\u00DC|H\u00E30\u0092i\u00B1\u0098\u00BE\u00F3Ca\u00BF\u00C7\u00F4 X\u00EF\u00E1\u00C5\u0080\u00C2\u008E1'\u00E6\x11\x15\u0096/\x13\u00B418\x0E\u00BE/\u00D4\u00EDU\u00C2_\x06\u00CA\u00EE\u00A1\x10\u00B1\x1E\u008D\u0080\u0093\u00A9t[@cHB\x1E\u00E1Z\u009C\u00D0\u00FE\u00C1\u00D3\u008D\f\u00AAl\n\u00EA\u00B3\u0098\u0095\x18\u0090\u00B3\u00E0\u0090\u00BE;\u00EF\u0083\u00A3\u00C6z\u0099\u009EnWW_$\f\u00B3]\u00B5QL\u00A2\u00E6O\u008C\u00C1\u00E9\u00A2\u008Dy\n\u0082\u00ED;\\\x05u\u008B\u0088+l]' \u00E3y4qFA[\u00BB\u00A2ibi\u00D0\u0093K\u0093}\u00CDm\u00EC\u00B5\u00EE\x16w+\u0088^\f\u0098#\x18\u00C1\u00B1\u00F1;\u00D5\u008EcPE\bB\u00CF\u00CD\u0088f\u00B9+\u00FD\x0E\u00B7\u00A5\u00DE>7\"\x0EC\x0F[\u008E>\u00D0\u00A6g;\x06\u00AE`\u00B6\u00C3,\u00B6]\u00F4a\u00C0{\u009F\u00FE\u0098}\u00F4\u00C1W\u00BE\x19\u00CD\x7FS\u0095\x01\x00\u00FE\u00CA\u00F5\x07\u00FE\u00F6O_\u00FE\u00CD_\u00FD\u00F2\u0095f\u00B7$wF\x1D\u009E\u0092\u0084\n~\u00F7\u0097\u0091\u00E8\u00B68\u00F5\u0096\x1A\x11\u00D7\u00CC\u00A4\u00A8R\x01\u0094*\r2\x1B\u00E49D*M\u00ED\b\u00A6}R!h8\u00CC\u009A\x04@\x12\u00FF\u00BC\x15\u00AFP\x0F\u00DB\x05Y\u00C5\x10\u00A8:\u00C8\x00%\x1D\\\u00D0\u00B9K\x0EG,\u0080\u00C7\x1E\u00BBCk\u00D7\u00E8\u00DD=k\x1B\u00CE\x19\u00FBB\x0E\u00BA\u00B8X\u00AEcI.\u00F2l\x0F\u00F2D\u00C59 \u00F4O\u0083\x19\x16U\"\u0089APZ\u00C7f\u00D9$\u00B2\u00BF\x13\t\u00A5\u009Djk\x1E\u00E3l\x01\u0080\u009B\u00A0i\u00CC\u00E8\u009C\u00C8=\x1A\u00A4\u00EF'\u00CC\u009F\u00B6\u00F6\u0093\u00CDHmI\x01-\u00FB\u0084#\x03\u0092\u00A4\u00C2H\u00F5H;Bs\u00D3f\u00FD\x05G\x1Av\x0F\u00D9r\u00A0:\x13~z>\u00D5\u008F86\u009A\u00A4oCms^\u0093\u00DF\u00D4\u00A8\u009Cs[\u00ED\n\u00D3s\u00BE\u00E3\u00D2\r\x19\u00D7\u0090\u0081G\x1E\u0084O.S\u00D5\u00A0f[{\u00EC\u00A07\u009AKw\u00B8\u00D4\f\u00A9%\x13\u0085\u00D69\u00B1T\u00DE\u00CA\u00E2z\u00C1\u00E3\u00AB\x1B\u00F8\u00BF\u00FFm\x7F\x1B\u00BF\u008Co\u00FA\u00FA\u0096\f\u00E1/\u008F\u00A7~\u00EE#7\u00EF\u00FC\u00EC\u00CF\u00E0\x0B\u00DF\u00D9\u0089\u00BFB\u0090\x15\x04\x04\f\u009C\x19\x02\x7F\u00A7\x04w%\x17%0\u00E9\u009F\u00F2\x18P\u0085PI\u00F4\u00D9\u009E\u00D6\x1B\u00DA-Z\u00FEB\u00DA::\f\u00B3\u00AC\\\u00E4\u00FDZ\u009C\u0083\u008D\u00F40\u00F5p\u0083\u00B0\u00B3\u00BA\x10\u00D0\u00A4\x0BO\u00F5R!\u00D2G,\x1F\u009F\x03\u008F\u00A7\u00E3\x06e\u0080;\u00DC\x18\x05\x18\u009Bt\u00D9\x01\u00EC\x13\u0094\u00CCqMU:\n\u00A9X\u0099\u008B\u00B18\u00E6\u00AC\u00C2*e{h\u00BA8U\x16K\x1Ff[\u00B8\u0094\u009C\u00C6v#Z\u00B06\u008C\x15\u00D2\u00B0\u00C9(B\x032C\u00E3\u00E4R3\tK1\"\u0080FB\x16f\u00E1\u00A9\u00AA\u00C4l@\u00DA3z\u00AD\x05w\u00CD\x01\x055\u00C7\u00A5\x10\u00EA\u00CA\u00DA\u008C\x1B\u00AAv\x04\x10\u00C7\u00D3\u0095\u00AAq\u00A3\x13\u00AC\u0097\u008A\u0090\u0084^\u0084\u009BL\u009F\u00EDed\u00A3\x18Bc\u00AAu\u008D-mZg\x06\u00B3\x11\u00B0\x03\u00ABZ\u00A0\x10dU[\u00AE\u00B9L\u00A9C\u00FB@\u00C6)\u00CC\u0081\u00AA\u00D1\u00A02h\u008D\u0089\u00B86wU5%\u00B4\u00DF\u00C4\x18\u00AEp<\x7F\u00FD\u00D9\u00A7\u00FF\u00A7\u00E7\x7F\x0E\u00FF\x1B\u00BE\u00E9\u00EB\u009B\u00AA\f\x00\u00F0\u00B4\u00D9\u00FC\u00C8|\u00EEo]\x1F\x01Q\x12\u00CE/\u00C4n\x18\u00BDNA\u00B2\u00C8P\x11\u00A2\x12\u00B1&\u00D4\u00DA\u00EF\x1A\u0082\n\u00B1m\u0091\u0081\u0088\u0088kp\x05\u00E0\u00A7\u009A\u00D0\u00F4\u00AE\u00B4\x1D\u00F0\x1E\u00CE\u00F3\u0096~\u00C4\x1D\u0082\x118f\u00E2\x0F*4\u00BEg\u00D4\u00D9\u00F2\u009F\u008E\u00F9Z\u009A\u00AC\u00A2NipL\u00A9\u00D1\u00E3\x10h\x01\u00A1;\u00EB\u00E2\u008EG\u0097\u0089\u008B\u00D4\u0080\u0093\u00CA\u0090P\u009Cn9\x1C\u00A5\u0093w\u0097\\@\u00E6\u00E6\u00AA\x13\u00FCOH\u00ED\u00B4\x1F\u0094}\u00E2\u00E2H\u00DD\x7F\u0085\u00DD\u00D4\u00DF\x1D\u00CD\u0085\x17\u00EE@EX\u008A\u0081\u00F5\u00FBkM*\x1AR\u00EE\u00D3y\x04\u0084?\u00A8\u00F6\u0094\u00AB\u0092\u00EA\u008A\u009F\u00FA\u009B}C\u00DA\u00CB\u00CE\u0091\u0089]\u008D\u0088\u00EF\u009E.V\u0085y\u00CFv^Fn\u00CA.\u00E1\u0096c\u00C5a\x01\u00F9\u00C5m\x14\u008F\u0080\u00CElPB\u0080j\u0080\x1D\u00FA\u00EE\u008BJ\u00D2\u00F1o\u00DA\x0E\u00C5\x00L\u00EB;\u00B8M\u00AA\x18\x13\u00E5\u009A\u0094^?\u00B7\"\u00F8)w\u00A4\u00D4\x04\u00ED4%\u00D5A\u00E7m\u00B0\u00B977\u00E75lR\u00A5P\u0096\u00A4m\u0080\u00B2\u0091m\u00C0\u0087\u00E1\u00D1w=\u00F1\u00B7\u00EC\u00DDU\u0094\u00EE\u008D^\u00DF\u0092!\x00\u00C0\u008F?x\u00DF\u00DF\u00F9\u009E\u009B\u00B7?,\x12\x161vC\u009F\u009D~\u00BB\u0082\u00FB\x0EW\n\u00F4\x1D\u0086\u0093\nF\u00EA\u00BA\u00BE8\u00EA\u00C9\u00D8\u00B2\u00F8j\u00E3\u00DE\u00A6{\u009F\u00DD\u008E\u00A8{\u00DA\u00E9\u009E]=\u00A9\u00FEJ5)\u00A2\u00EF\u00D24%\x0F\to\u00958\x12%\u00A0O[a\u00BA\x00\u00A8\x0F?\u0086\u00E1\x11\"\u00E6\u00FFp\x11\u008Dt\u00FC\u00B1\u0084!\u00CB\u00B7~\u00D1Bg\u00A8\u00F3b\u0094$\u00A38\x1C\x19\u0097p\x1CE 3u\u00FA5\u0094Y\be:\u00D5\u00E6\u00C6h\u0082\u0090*m\u00BA\u00EE\u00AF\u00F3\u00C2\u00C8\u00B8\x18&\u00F3\u00FA\u0095a-\u0084\u00DD\u0098[o\u00FB\x1C#\u00B1\u00F4\u009F\f\u00A9\u00DBJd+\u0088\u00A9/uj\u00E6\u00EF\u00EDoh\u00BC\u00B4\u00A7\u00F07x\u0095\u0099\u008B\x07\u0086\u00E5\u00B7\u00C5\u009B\u00C0\u009BIUY\x10\u0083\u00C8\u00CA\u009D5\x1B$\u00B1\u00F5\u00AEPmE\u008E\u0096\u00910\b\u00DB\u00E7\x06?\x18\u0082,C\u00A3\u008C\u0082\u00F9\u00EE\u00EB\u009E\u00B6\u0083\u009E6\u00ED\u00BAFo\x1A3q\x05\u00D9\x12\u00E0;\x1E}\u00FB\u00FE\u00F0\u00D9\u00BF\u00F6\u00A1\u00BF\u00F3fh\u00FD[\u00AA\f\x00\u00F0A\x1B/\u00FD\u00F5\u00D7>\u00F9\u00F7\u00FE9\u00BE\u00FA\u00DF\u009F\x0B\u00A3\u008C;\u00F6^\u00C8\u00ECC\u00EE\u00F8\u00B4\u0086 \u0087\x14\u008E9\u00ED\u00A9\u00D2[\u00D9\n\u00A42\u00C0\u00A8:\u008D<\x1E\u00BFm|v]E8C\u00FF^\u00BB@\u00CF\u009F\u00AA\u008B\u008EY\u00FBu6i\u0081\u00A6G\u00FBz\\a\u00B3g\u00BD]\r*\u00DC\u00B6*\x00\x01\u008F\u00CC\"\u0094\x19\u0086\u00CB\x04vI\u00B9i\u00B5A\u00AC\u0087\u00F1m\u00D3\"d\u00CFv\x07z\u00D5\u00E1\u0088;\u00A8\u0084&\u00A1\x13\u009F\u00EB\u00B6lZ\u00E3\u0087G\u00A8o\u00BA#\u00D9\u00BFHk\u00B6$\x06\u009B\u0081\u0086\u00E4\u0096\u008C\u0094f\u00A2\x1E\u00B6\x0B\x04mt\u00FD]\u0084\u0092\u00FB\x1A:\u00FB\u009E\u00E8)'\u00BF!\u00AA8t\x00-6A*R\u009D\u00A3B(\u0080e\\B\u00CE/\u0090\u00E3U\u00FF\u00F4\u009B!\u00FA8\u00C8p\u00C0\u00F6\u00C11\u00AEj@t\u00CE\u00F9\u00BB\u00EE\u00DD\u00ABUk\u008E\u00D3k\u0091s\u00A6\u00E7oy\u00AC\u00E7\u00B4\u00E4\u00B2\u00E8\u00F7\u009A\u00DA\x12N\u00C2\u00D49_\u00E2\u0098\x1Bl\x0E8\u00EB5z\"\x1A\n?fP\u00AArs\u00C6.\u00A4RF\u00F5.V+\u00DC&.\u00EF\u00BF\u00F7\u00F7\u009E\u00F8\u00CF\u009Fx\to\u00E2\u00F5\u00A6\x10\x02\x00\u00FC\u00C8\u0083\u00F7\u00FF\u00CD\u00F7\u00DF<\u0085\b\fVRQW\x11\u00A2C\u00DE\n\u0099\u00ACII\u00F5\u00D9\u00A3\x0E3\x01#\u008D&N\u00FA\u0097~\u00DF#\x1D{\br\u00B7\x13\x14\x17U\u00BF\u00AA\u00BE\u0082\u00F3\u00DF\u00D1\u00EE\u00D9\u00E2#\x16\u00CE\x0E41T\x0B\u00DEyXPu\u0091,@F\u00BFI\u00C5\u009B\u0088\u00AD\u00C7\u00F8\u00FD8\u0080\u009B\x19\u00EFC\u0089m\u00CDr\x1FR\u00D3R\x15\u00C9\u009C\u0087\u0094\u0098k\u00F4\u009FP\u0080\x13\u00D2V\x04\x1Fc\x1E\x1C\u0098\u0097RC\u00E6,(/F2\x19A\x18\u00EB\u00D0Jb\x1F\u00CA\u0083@F\x18\x1E\x07\u008B\u0096\u00A8\u009D\x03\u00B7\" \u00FD\u00F0j\u00CB;\u008C/\x17h\u00CE\u00A5\u00BC-\u00BE\u00BAC\u0085~\u00D2\u00BD\u00E9B\x1D5\u00B7\u00D3\x03a\\\u009A\u00E7\u00A4\u008F\u00CD\u0081\u00F4\\4 \u00B0\x18g\u00B1\x100\u009F\u00D7\u00AC\u00AC\u00C7s\u00BD\u00C9d\u008Ah\u00F8\u0092\fd\u00AA\u00ED\u00E9\u00CB}t}\u00A8\u00A3\u00DE:'\x06\u00D2\u00EA.\u00CC\u00EEy\u0090\x0B\u00F1\x1A\u00E6\u00D7Eg\x1E\u0095\u00CA\fJ\u0096:#k\u00EER\u00ED\u00FCN\u00DAx\u00F5\u0099k\u00DC\u00FF\u00D1\u00F7\u00FC\u00CD7K\u00E7o\n!\x00\u00C0\u008F\u00D9\u00D3\u00BF\u00F1W_\u00FF\u00C4?~a\u00DE\u00FC%\x1BgO\u0082ES^\u00F6\u0085\u00D0\u00C5\x03\x19\u00A4z\u0095\u00E8\u00A1\u00A9\x19D\x0F.\u00C9\u00D2\u0083\u008AT\u0084%#\x19\u00CF\f&\x18R$p\u00F4k\u00FB\u00EB\u00B6\u00E1Pq\x0E2\x02\u00F6\x12\u00E1\x19\u00BF\u00DE$\u0095\u00A4V\x04\u009E\u0085\x14\u0088`\u0098Z\x1C\u00A5n\u00B4\u00DCz\x18\u00C2\x1B\x11\u00DE\u0086\x0B\u00DF74,\u00CE\u00C9uC\x1D\x1A@\u00A9'(\t\u00D8%\u00B2\u00E0\u00B4\u00A4a\x1A\u00D3\u00DA\u00F7\u00F4&L\u00CB\u00FE\u00A9M\u00B5\u008FI\u0097)\u00BF\u0087\x0Bn4\u00A4TFC\u00ED\u00D1\bXz\f\f\u00C1%g\u00DAR\u00A2\u008C]&?9\x18x\u00E4u]RL\u00A1\u0085\u00990\u009D\u00FD\u00E4\u00E7\u0098\u008D\b\u009Bd\u009Em\u00ECzV\u00D3#ZQ\x06[m\u00A7&/P\u009FG]\u00A7<\u008F\u0090\u00FE\u0096\u00EDg9\u00B6Y\u0092\u00BE\u0090\x04\u008A\u00D0i[\u00B0\u00BE\u00DE\u00D2\u00B8\u00A8\u00F9+\u0086S\fA\u00CC\u00A0\u00AFQ\u00A9\u00C9\u00E2\u00C4\u00E2nz~jw\u00B6s$\u0081,\u00C7\x11\u00D6\u00E2cY\u00C0\u00F6\u0081\u00ED\x1F_\u00FF\u00CF\u00DF\u00F6\x1Bx\u0093\u00AF7\u00CD\x10\x00\u00E0?\u00DB\u00DF\u00F7\x13?{\u00F3\u00D5\u00BF\u00F4\u00D5{\x05M\u00E2E\u0097b\u00D6.\u00E8jD\f\u00C8\u00AC%'5\x17\u008A\u00AC\u00C1\u0090\u00F1\u00F0\x1CD\x04$\u00F3\u00A80\u00E5\u009E\x04E\u00E6c\u00AA\u00B0\u00DC_2\u00FEu\u00B7(\u00EF-\t\x00\u0084\u008E7\u00FB\u0083m\u00C4\x02-@\u00BF\u00E3\u00FB\u00AA>\u00A4\u00CB\u00AF?|\u00B6\u00F3\x18\u00C1\x14\x1E\x1C\x01\u0095/\u0088<\x07\u00A3\u00CF_\u00D6\u00F1\u00D4\u00A3\x1B\u00F1az\u00EE\u0087xq`\u009B\u008E\u008B\u00DC\u00A2\x1E\u00A1\u00C088#\u00B3\u00AEs\u00AB\u00FE\u00C8\u0095\u0089\u00C9\u00DD\u009C\u00DCr\u00BE\u00D4\u00CE\u0098m\u0083\x16W%\u00E75:q\u00A2\b\u00E5jJ\u00AD\u0088\u00F1O\u0094:\u00A3\u00E4\u00A0,\x18\u00CB{\x1Cd\u0092\u00C1P\x18\u00AB\u0092\u008C\u00AC\u0092\u00A5\u00AE\u008EZa\x07}\u00FC\x06\u0086\\\u0093\x01\\<\u0098\u00D5v\u00A86eA\u00FB!\u00BF?j\u0099\u00CA})\x0F\u00CC\u0098\u0094\u00FE\u00C9\x04\u009D0\u009D\u00CF]\u008C \u0099G\u00FB{A\u0091d\u00A8\u00CD\u00D8,\u00A6\u00D0\u00ED\n\u00B9\u00BC4g\u00BC\u0097\u00EADD?\u0085\u00BA'\u00B2|\u00FA\x042\x14Y.\x1BeWB\u00A8@\x0B\u0091\u00DE\f\x00\u008F\u00AE\x1E\u00C3\x7F\u00E8\u0099\u009F@\u00EE\u00DC\u00F8\u00AD_gq\u00FAM__p\u00B7\u00FF\u00F1\u00E1\u00BF\u00F8\u008D\u009F\u00BE\u00FF\u00F2\u0087c\u008EE\u00A8\x1D\u008E\u009F\u008Cx\u0092\u0094}#\x14\u00D9\x03\u00BAo\u00F5\rK\u00A2\u00A3\u00CEK/\x02\x11\x01\u00CF\u00A9\u00A8\u00C8\u00AC\u00AF\u008B\u008E\x04\u00EA\u009ChK\x10/\u0082\u008BJ:\u00A0\x11\u00B3\u00A4d\u00D7\x19\u0081UZ\u00A9\u00A8\u0088\fG\u00E5\x1A\u00E4\u00EF\u00ED\u0081\x0E\x07\u009Eq\u00E0-\u0087\u00E3\x01\u0080{0\u00DC\u009F\u00C0\u00B5\x03\u00F7\u008E\u00B0\x15\u00EC\b\u00F7\u00DB\u00EE\u00F1\u00B9\u0091HwJ\u00DC}rvf\x10\u00C66\u0099\x10>\u0083I\u0084W\u00C4*L9\t=\u00DAQ.\u00C38(\u00BD\x13-\x00v8\u00FD\u00F9\u0096\x12iL\u00C7\u0086\x11\u0095\u00A3\x01dL*\u00DDd\u00D7\u00B3\x14\u00C3m\x02c \u00E3\b6\u008Fcz\u008A;\u00DD\u0085\x0EVS\u00E28\x06\u00DB\u00AF\u009D\u009D\u0082\u00D8\u00AFy\u00AD\u00F3\x19mm,Q`\u00A5\u00C68fI\u00FD\u00A5\u00F8\u00AC\u00BE\u00EB^\u00B3\u00EE\u0093L\u0081RX\u00A8\u00A9\u00F6@Q\u00B6(V\u00E2g\x00S\u00E9\u008E\x03r9\u00CA\u00DD\u009B\x06\u008E\u00A3\u00AEW\x1E\u0086\x1D1\u00B7i\u00B3\u00D09\u00D2K\u0099b\x1D\u00C5o\u00BDt2\u00EA@~\u00A8D\u00FB\x05e\u00DD\u00E5\u00FD\u00A2Q8\u00E1\u00EE\u00EB\x1F~\u00F4\u0089'~\u00F6\u00F9?k\u00EF\\\u00F1\u00C87{\u00BDi\x1B\x02\x00\u00BC\u00CB\u00CC\x7Fx\u00FF\u00B6\u00BF\u00F1\u00E4\u00A5\u0092\u0095\x14\x15\u00A8B%\u00A1&4\u00A2dm\x02w\u00C3R\u00CC\u00C4\u008BX\u00A1b\u00A7\u00EDx\u0096a\u00EF\u00E7\u00C9\x0B\u0081\u00D2\u00BFz\u00B0\u0091\u00AE\u00CB<\u0089\u0093\u00CD\u00A0\u00B6O\u00F3|\u00C0\u0081\u00C4\u00BC8\u00B1\"\u00D6\u00E6\u00CA(\u00F4\u00C0U\u0088T\u00EF\u0092\u0086\r\u00EDH?\u00CC\u00C5\x13\u00F7y\x1D\u00C0CC\u00BA\u00F6z\x01\u0095\u00D9\u008E\u00F5\u00D0by\n\u00E0Ul\u00E4\u00A0\x15;P\"\u00ED\x0B\u00C7H\x17\u00E3\"\u00B8\u00B2-E0\x06\u00C4\u00CE(F\u008D+\u00C3\u0096U\u00B8\u00C5\u00E9\u00D9\u00C0\u00E2\x06\u00D4y\u00EE\u00A3\u00DC{i\u00C3P_*\x13\u00D3a\u00E5\u00D9`?\u009Czs\u00B4\u0081\u00C5~\u0090\x1E\u008F\u00E5]\u00E3\u0090\u009A$\u00D5\u00A9\u00FF\u00DD\u00DD\u009A\u00B7\u008A\u00D3\u009C\u00BC\x185'm\u00AE\u0093p\u0091\x02\u00A0G?:\t\u00B5\u00EF@\u009D\u009E%/4V\u00D9\u0095\u00ED=\u00D1\x10C\u00AC\u009D\u0085yt\u00C6##\"6D\x14\u00A2\u00EC\x02\u00F1vlp\u00BF\u0082\u00DFU\x00\u0096\u0086\u00FC\u009Bk\u00C7\u00CDG\u009E\u00FA\x1B\x7F\x10f\x00\u00FC\x01\x11\x02\x00|\u00D9}\u00FBk\x0F\x7F\u00FB\u0093\u00FF\u00EF\u00F5W>t\f@\u0084*&0\u00E8\x06\u009CwH\u00E8\u0098$\u00C1w\u00D9\b\u0090D,\u0084`\u00A7k\u00AB\u009B\u00C1|\x06\u00B4\tK\x1C\u00F7\u0085\u00AF\u009D\u00BC\r\x10\u00E3@F\u00E7\t\u00CA\u00A6\u00DE\u00CA\x18\u0091\u00E4\u00E6\u00FC.\tu\u00F66T<\u0082PC\u00B5\u0095\u00C5R\x1A\u00AC\u00D4b\x1A0\u00BC\u00D5\u0081g'\u00F0\u0084\x03\x0Ff8\u0088\u00EEM\u00E0j\x06L\x1E\b\u00E98< \u00F9\x15\u0083\u00906\u0084D\u00DB&35\u0088\x18\x14\u00F7puXIZ/\x14\u00E1\u00EE\u00D8\u008E\n\u00D0\u00D9fH(\x13b8\x06\u00B6\u00A3\u00A4\u00D98\x1CR\u00E5\u00C6\u00E1\u00CDL\x0B\fL\\90h\x19\u00DF<\u009E\u0086\x01\u00D8|\u0086\u00A9\u00B9\u00F5as`X\u00AC\u008C}VV\u00E2F\u0084`>R\u00C2\u0083c:\u00BFAuG\u0088 \x11\x00b,\u008AR\x14JP\u00E99!\x00\u00A9Q\u00FD\u00F7\u00B4\u00D3\u00B4~voEHr\u00AA;\u00ED\u00B8\u0093\u00AB\u0099PT\u00DA\b\u009A\x1A\u00AAR\x05D]\u0081*\u00EC\u00842<U\u00C9\u00FF\u00BF\u00BDo\u008F\u00BA\u00F4\u00AC\u00EA\u00FB\u00ED\u00E7=\u00E7\u00FB\u00BE\u0099df2\u0099\u00DC\x00\t\u0082@\u00B8#wQ,\u00B1,\u00AC\b\u0088\u00B5\u00A6\x0B\u00A8\u00B4R\u0090.\u00DAJ\u00ADE\u00B0\u00DAbU\u00BCVY\n\u00AC\u00A5b\u00B5\x15X\u008A\u008D-\u00A2(\u0082J\u00B1R*\x17A@0\x01\u008A\x10\b\u00B9\x13&s\u00F9.\u00E7\u00BC\u00CF\u00EE\x1F{\u00FF\u00F6\u00DE\u00EF\u0099Q\u0099\u00CC$\u0099$\u00F3\u00CC\u00FA\u00D6\u00F7\u00CD9\u00EF\u00FD}\u00F6\u00ED\u00B7\x7F{?\u0093\u00F0\u00A3\u00FE\u009F\u009C\t\u0085e\x15\u00C6\u00B1P\u00A7\u00D5\u00BD\u00881\u00BD\x05^\u008B\u00EF|\u00F4\u00E1\u00E3\x15\u00BB\x7F\u00E7^\x0F\u0093\u00FB\x10T\u00F8\u00F2\u00C6\ta\b\x00p@d\u00FC\u00A9\u00C5\u00F5\u00AF|w?\u00F8\u00C6[D\u00A12mJ:u\u00D5Kh %|\x00\x10x\u0081\x00\\oO+-\u00B9\u0080\u0089S!?\x1E\u009F\x00Hg\u00E7X\u00AE\u00C1\u00B4BQR\u00F8G\u00FFC\u00A7\u0096>\u00C3\x07\u00B8\"H+\x10\u00BD-\u00A9\u00E9I9\u00E5\u00A4)\u00E7b,J\u00C5a\u00BC~\u0099\u00E4\u00F8E\u00DC\u00DD\u00A6\u00FB\u00DA\u00FD\u00B2h\u00ADy\u00AEn\u00E9\u00C9UPMz.\u00B1\u00CE\u00EB\u0092\u00B2O(3\x07\u00B9\u00A4+0f\n/\u00C2\x06\u009F\u00D8\u00AAE\x10z}\u00AF\u0080\n\x1B\u00B1\u00F8\u0092!\u009Ao\u00A4\u00FB\u00BB%\x00\u00DA\u00FC\u00D1v\x17l\u00A68\u00A3\x0B3\u00F2\u00B92M\u008A\u00F2}Z\u00D6\x12\u0087\x03\u0093\u00F2\u00EAi\u00F5\u00A6WN\u00D6\u0090A\u00CB\u00B3\u00F1wA\u00F7\u00BAi\u00A6 \u00A3\u0081L(\x043I\u0083\u00F2\u00C6\x11\u009EA\u00D3:\u00E7$CE\x1A\x01\u00E2\x01\u00EEm\u00C4w|/~\u00F3\u0089\x1Bh\u00CC\u009Fc\x7F\u0088\u00E7\u0090\u008BS^jP\u009D)W|\u0089\x03\u0096\u00F3\u008E\u00C5c\u00F1\u00CA\x13U\x06\u00D3\u00B7}\x02\u00E3\x0B\u00BA\x18^\u00B4\u00F9\u00E9\u008F\u00BC}\u00FD\u0096\u0087,\u009B\u00A5\x19\u00DD?\u0080\u00A0\u00D9\u0082\u00A0H\u00D7\u00DF\u0098\u0084\u00C5\u00CA\u00B7\"\u00B4\u00D1)\u0099\\\u0085\u00BA\u00B0j\u00C1\n\u00FC\u00B3\u00D5^\u0087\u0088\u00F0@\u00FC\u0081\x14\u00BC\u00A0\x1B\u00AA\x10\u008A`Uh\u00FD%M\u0084?0\u0080\u00E2:\x16w/\u00F6\x05\u00C2\u00D7\u00CC\x05;}\x7F\x0F\x1B\x02\u00E7\u00E9y\u009E55\x0Fa\u008F*vw\u00C1\x1A\u00CCCX\u00EF6\x01\u00D7F\u00D8\u00DA\u0090\u00DD,\u00E9\u009Ag\n\u0086\x11\u0098+-\u00A4\u00FA\u008APn\u00FD\u00FC\u00F3\x16\u00F7c\x13w\u00E6!K\u00C4\u00CDj\u00C7i%\u00B6m]18\u00D3\u00AE{\u008CKv\u00A68.1\u0083\u00BD\u00B2\x06\u00BB\u009E\u00D65\u00B1\x03\u00A0`\f\u00C9'\x18\u0090\u00D6\u009A\u00D7e\u009EB\u00BE9Z\u00E7Yw\u00E6\u008A\u008Ay0\u00E58\u00D2\u00BB\x0B\u00AF\u00C4\u00B1\x1A\u00EF\u009Fx\u0089\u00DF[S5\u0080U\u00E1\u00CA\u0095\x19\u0090\x04\x1C\x07\u00EF\u00B7@\x0FJ\u00CA\u00BBi\u00E5o>\u00F3\u00C8.-\u00F9~\u0085\u00DAob\u00FDY6~\u008C\u00B5WD\x19\u0083yd\u00A6\u0090c\u009B\u00C0\x10\u00CA>\u00AB\u009F\u00BB\u00B7`J\u00BAC\"^r\u00ED\u00989h\x00\u00C0\u00D1\u0087\u008E\x1F\u00DF\u00FD\x1B\x07\x1E!\x0F;q\u0085p\u00C2\x1E\x02\x00\u00DCS\u00E6\u00E3kw\x0E\u00FE\u00D0\u00FF\x19\u008F^~s\u00AB\x00\u009E\u00CB-\u00B9\x02\u00E5sS\x04\u00C5\u00DA\u00D3k(\x05Q\x1A\u00D9\u0086\n.\u00C2~\u00C7*\u00CA\u00AB\u00CA\u00A0z#\u00A5i+s\u00CAD\u009Bu\u00E5\u0085izY\u0093\u0094\x1C\u00B5\u00B8 4=_\u00A0\u00D2\u008CUE2A\u0092\u00E1J\b\u00F1\u0082\u00EB9\u009B\x02;\u00AA8\u00D2\x15k\x00\u00E6\x0E\u0096Y\u0097e\u00EF]\u00E0\u00E7\u00E9n!\u00BA\u00FA\x1D\u00AAF\u00AE=\u00D2gL\u00CDi\u00C6\u00D7\u0083O(\u00C6\u00CESo\u00C2~:\u0080\u00ACe\u00E0\u00F5i(\u00CC\b\x7F\u00FC\u00F3.\x06~u!\u00C5Y\u00CB\u00DB\u00A1\x153o\u00A7\u00BB{\u00DF\u00F9\x19\u00CA\u00F9a\u00E0'\x1F\u00CF\u00A4E\x1C\u00DC;p7\x7F\u00D4$\x13\u00B1AK\u00EC\u00D8\u00D3\u0083\u00A9\u00DE\u0082yq\u00DE8\u00A5\u00A3x\x13\x02\u00AEX=IwvL<\u0085\u00CE\u00E7\u00A7\u00E4\x14\x14\u00B7\u009Es\u0089\u0083\u00DFQ\u0081\u00AC\u0086\x0F\u00FE\\\u00DD9,D'\u00C99\u00C1\u00FD15\x1C\u00AA9\x1F'\u00DE\u0082\x02\x1AM[\u00A9\u00A9\x04\u00C0\u00C2\x1D\x13\u00C1\u00CE\u00DA\x02x\u00FC\u00EC\u0087n\u008D2\u00C8\u00B7y+\u00C6-\u00AA\u00F2\u00DC\u009DO\u00BC\u00F7\u00F7\u00D6\u00B6\x1E\u0097\u0082\u00EF\u00F1:\u00D7l\u008C\x07\u00E8\u0082\u00DD\u00DC\u00A6(\u00C3\u0085\u00B2H+\u0095D`\t5T\u00A8\u00DD\u0099\u00EDxZ\u0095E\u0080\u0087vK\u00C7\u00B8g=_^6\u00D0\u00F0\u0085T\u0090/+&KW\u00D0E\u00D3N\u00E4\x1D`<g\u00FB\u00DB\x04\u00E9\u00A3\u009D\u0093\u0096yBs-\u00EE)]JU`C;\u00CE\u00E9\u0082\u00B3 \u00D8\u00D5\u0081uu\x1CA\x15k\u00A3+\u008Anw9Wz\x02\u00863\x04\u00B2\u00AE\u00F6\x7FNh\u00C6\u00D7\u009C\u00D0\u00D2\u00BD1\t\u00F9\x06\u00AE\x00fK\u00BB\u00DE(\x1E\x1A\u0081\u0099s\u00A7\r\bM7wX\u00BA%\u0096\\Lf\u00EEnx\x13s\u00D1\u0087\u009E\u00DE@\x13\x04\u0086\u00D0\u0080(^b\u00E6A\u00A0\u0098\u008D\u00E5\u00AD\u00BA\u00C0\u00F2\u00FEV\u00BD\u0089yX\u00F0\u00E4S\u00D0\x1B\u00AAx\x020\u00C5\x11f\u008E;\u00B0n\u0086a\x15\u00D4\u00EE5\u00B1\x03Stl:\x03\x0F\x13\u00E8\u00E5\u00CD\u00AA\x07\u00C2y\u00A3H\f@\u0091\u00C0\u00B1\u0083\u0089\u00B6,^z\r\u00AB\u009E\x00\u009Fy(\x12b6,\u00AF.JH\u008A'\u0097\u00D8U\tk\u00BB?tGL\x15\x1D\x07\x1F\u00B9\u00F5\u00FEs~k\u00DF\x13\u00E4\u00FE'\x06&r\u00DC*\x0F\x01\x00\u00F6\u008A\u00E8\x1B\u00B7o|\u00F9\u0087v\u00AE{\u00E75su*\u0081e\f\"\u00D4\u0081\u00C0HLH|@\x05\u00D3\u00E5\u00D3\u0080\u00C4\x13*F@\u00F2\u0092\u008FP\u00BC5dhHI\u00CD\u00E6\u009C\u00B1\u00EE_<\u00B8bE\u0080\u00D0\u00A6\u00E15\x10h\x04s\u00EC\u00C4\x04\u00FC\u00C5\u00FAY \bB\u008A\t]sk\u00CD^\u00FE\u0092\u00D5\u0099\u00DD\u00F7GN\x0E\u0085\u009Dk\u00D4\u0086m(\u00D6\u00BBy\x07\u00B3\u00EE\x14\u00E3n\u00B1v\x02j\u00B6^\x03'\u00D2\u00E8B`\u00C8=2\x06.\x7F\u00F3\u00BA\u00B3S\u00F3\u00D4\u0083ag\u00E5\u00D1\x05\b=\x17\u0098%\u00D0H\u00CB\u00D8\x1C\u00C3\u0080p\u00AD\x05\u00BF\u00D7\x15\u008F\u0090\x1E\t\x04\u00B1\u00B8\u008C\u00DDj\t\u00F0\u00BA=_\x12\u0091\u00F8.hY\u0089]\u00D0]\x17\u00B5y\u009Ex@z\x1B\u00DD\x15\u00DF\u0088\x14T\u00A1\u00DB\u00DE\u00E9aK(\x05\u00BB&\u00C7g\u00A8@\u00B9\u00AF&\u0081\u0089\u00EF\u009F\u00CA\u00A6\u00FB\u00FDFj\x12\u00F6\u00BC\u00A4(\x03\x1E\u00C3\u009E{\u00CBgM%BY\u00A8\u009F\u0097\u00BF9?\u00E8\u0081PaLZ\u00E1\u00D1\u00B0Q\x10(\u00EA\u008D\u00A1\u00F4\x004\u00E0\u00E8\u00EE\x05\u0086\u00AF\u00C7\u00CBo\u00AD2\x00\u00EF\u00F1d\u00C6e\u009B\u009F\u00F9\u00DD7\u00AF\x1Dz\u00C62\u00AC\x7FI\x15\u00D2\u00D2\u00D76d%\u00930\u00C5\x0B8u\n\u00E9\u00C8\u00A5\u00D0X\u00C8l\u00A1\u00B6\u009AE\x00\u00B2\u0097\x1C\x02M\u009F<h>\\\x00\\\u00D04<\u008A\u00F0\"\u00F8}\u00BA\u00E15\u0093\u00C0\x14\u0093L44\u0095\x032^\u00E4dA\u00F1\f&\u00B9f\x13\u00F85(\u00CE\u00E9\u00C0Y*\u00D8\u00E8\u008A\u00F5\u00AEX\u0083`\u00DE\x05kjX\u0082hf\x10fj\u0096tN\u00A1W\u00B3\u00A0D\u00C8E\u00BD\x0E\u00C2\x05\u00BB\u008D\u0088\u009C\u00BD\u0091\u009A\u00E8!\u00F8z\u00C7=\x17Y\u00CD<>-\x1C\u0080\u00D1\u00B3\f\u009A\u00A8\u008C\u00C00\x10Q\u00F3\x10\u00CC30\u00A0\u0091*bp\x014k\u00EFX\u0083\u00FA[\u00F5{h\u00F4P\u0090\u0082>\u00A3\u0087\u00D0\u00E3\u00ED\u009B\x07\u00E4\u00CF\u0095\u00DB\u00E7=\u00E5s\u0080oKk\u00CF\u00E3\x0Eu\u00DF>\u00DD\u00DE\u00E9^\u00CF\x00\x00 \x00IDAT\u009F\u009C\u0084\x00\x18\u00C19\u00A2Q\u0092NrS\u00DD?\u0095\x00\u009C\u009EN+\u00C3\u00F0\u0092\u00F3`E!\u00F0\u00EFR\u00C1\x1C\u00DC\x07-\x1C\u0086\u0092\u00AAF1.\x18\u00CD\u00DBY\x05Y}\u00D2\u00A2Cq\u00F81\u0087\u00DE\u00BA\u00EF}\u00FB\u009F\u0089\u0093\x18'\u00C4C8\u00DE\u00F8Gk\x17\u00BE\u00F4\u00BE\u00CB\u00B5E\u00AE\u009E\u00E3\u0082\u00DA\u00DC\u00F5\u00AF\u0099\u0082P\x06\u00B9P\u00EA\u00DF\u00DC\u0087\u00D1\u00D8\u008DT\x06S%P\u008E\u00C9\u00C53\u00F8\x10'\x0F\x14\u00A1\u00A9\u00834T\u00E3\u00C2\u00F2\u00BD\u00D22N4\u00BA+\x17\x02I\u00DD\u00CC\x0FIK\x00\u00F2\u009E' Vz\x17S\x1C\u0081q \u00B0\u00E8\u008A\u00EDn\u00F5\r\u00CBnU\u008D\x0B5\u008EYt4\u00F2Sv\u00D5\u00B85\u00B6u\u00CF\u00D2h.\u00F6Z\u00F2\u00EF\u00AC'\x00\u00B9\n\u00AC3\x00\u00D8M\u00A9\u00E6\u00F5\u00AD{\u0091:\x7F@\u00A3\u00CEbY\u00AE\u0081\u00CF\u0084\x19\u00CA\u00B1\x1CgT\u00A0\u00C3R\u00CD\u00C6\u00AB\x11_\t\u00C0+(\x15\u00D3\u009F>\u00BD\u0087z\u00AF\u00BC\u00CE\u00E8\x1EU\x7F\u00C6\u00E9\u00F7<V\u00E5I\u00F0\u00F9\u00F5\u00E3\u009D+\u00EA/4\u00CE]\u00AF\u00A9\u00FE\u009Ep\x14\u00FA\u00CA\u00F5i\nu\u00E0\x06uN\u00B9A\u00E0\n\u00D9\u00F5\u00DE#4\u00AD\u00FBW\u00A3\u00B5b`\u00C0PZs\u00FE\x00\u00F5xv\u0096\u00ED\x03\u008B\u00C5\u00C67o\u00BC\u00F4$D\x19\u0094\u00B4\u0093\x1A\u00CF\x1Ev]\u00F9\u00D4q\u00EFk7\x16\u00D6\u009AL\u009Ada\u0091\n\u00A6e\u00CB\u00ECe \u00F9\u00FF\u00F2S3\u00DE6\u00DC\u008B\b\u00CC`JY&\x01\u00A8>\u00ECI\u0098@+0\u0099\u0090\u009E\u009ErA\x11>tz\x14\x14\u00F8\u00D1[\u008AGx\u0080p\x1Dc\u0081\u0095\u00A2\u00E9'/\x18\u0098Z\x06?\u008FY'\u00EFu\u00A8\u0082E\u00B7~\t#\n\u00C1\u0087\u0093\u0096\n\u00CA\u00BD\u0094\u00DA+1\n\u0086z\u00AE\u0094\\\u00E3L+\f\u0092\u0089\u00B0\u0090t\x13-\u00E1W~\u0082\u00E8\u00A4t\u00EB\u00CD\u0085\u00ED\u00FEy\u00F6&pr\u00CE\u0098\u0082be\u00D8\be\u00ECd;?&\tS\u0095p\u0084\x00=\u00B34\u00DA\x15\u009Bf\u008F\u0088\u00C9J\u00D5\x14\u00FA\u009E\x02?!v\x01HR\u0090N\u00AF\u00ADk(\u00CFT\x06\u009A\u008A\u0096\u00C2>\x02\x18]\u00D1)&\n\u00A1\u00D7s\u00B9qQ\u00E5\u00CA\x14\u00F9\u00EE9\u00C7\"\u00E4\\\t{\x10j\u00C24\u00CBj\u00B3\x15\x14\x0F\u00B3*\u008C8\x07\u0087\u0094\x10B\x04\u00CBG._\u00BB\u00FE\u009Fv]y\"\u00B2{\u00BCq\u00D2!\x03\x00|Hu\u00DF\u00BF\u00D8\u00FA\u00EC\u0095\u00EF__^h\x12C:3\u008B\u008E\u008A\u00DEQS\x1C\x13&!\u009B\u0099\u00CA\u00AA~\u00E2\u00A2+\u00A9 \u00C8\x17G\b5L`+\u00D0B\u00CE\u0080\u00A7\u00C7\u00A6\u00F1\u00B3_\u009F\x0B9\x00LR\u008AT\x12\u00AE(\u00A84\u00E2<NJQ`\u0092\x1A\u0092z\x0E\u0086\x1D\u00D5\u009DT\u00EE\u008F\u0088o\x07\u00ED\u00D8\u00A7\rg+\u00B0\u00BB\u009B\u00DB\u00BB\u00A6FL2r\u0092Ze\u00BB\u0083\u008C\x02\u00A35\u00B7Q\u0093\u00CA\u00ACL\u00A1I(@*\u00A8A\u00E1\u00EE\u00AFZ\b\x00\u00C7\t\u00E85\u0095<y\x0BE\u0098\u008A\u00ABy\u00F8@\u0095-\u00F0\u00B0\x04\u00A6\u00BEg0\x07P\u00B4\u00D0\u00AA\u00D5B[\u00BA\u00EC\u00E6\u00C2\u00DB\u00C2,\x06N\u0096\x18^\x13\u00C4\u008A\u00B2\u00EF\u00CE7.Q\u00CF@ u\u00F57\x14\u00C1\x15\u0098\u00D1\x00\u00F0\u00D8\x05Le(E\u00F7?0\x1A\u00C0(\u00DB*\x18\u00C6\fC\u00E0\u00F7He_\u00C3\u0096\u00D6\u0093\u00E2M\u00CC%:a\u008F\u0089A\u0098\u00C6*\x1Eg7U\u0080Q \u00DD\u00975\u00E8bD'*\u00FE\x12\u008E\u00847\u00CB\u00FD\u00F9\u009Bb\u00C49\x0F\u00E0\u00E8}\x0E_\u00B7\u00FB\u00A7v_\"\u0097\r\x07\u008F+\u00A0'0N\u00DAC\x00\u0080G\u0089\x1C|z;\u00FBe\u00FB\u0096|\t9\u0085\x12?p\u0090P\u00ACDzZ\u00BA,\u00DE\u00BB\u0080\u00A3x\x01!\u00FC\u00CD\x18\u008A<\u009E?d{!R\u00C2\x02{\u0088\u008D\u0093\u0080Z;\b!\u0080\u00E5\u00E9\u008F\u00E3\u00DA\u00F7\u009Cp\u00D5\u00F2s\x02\u00B4\u00E8e\u0097aH\x02\u0080~\u00E5.d,&\u00AAu\u00FCV7!\u00A1\u00FC\u0097*\u00D8T\x0B\x03v\u00BA\u00FD^\u00AA7[U\u0096\x1C\x0B\u0096]\u00AD5\u00FB\u00E8n\u00B3\u00B2\x11*i\u00C1i\u00D1\u00AA+l\u00EE\u00BD\u00AFD\rZ\u00C5\\@%\u00D6R\x1Ck\u00B92\u00BC)\u00AAL\u00AD\u00A3{\x11<\u0087*\u00DD\u00FD\u00F4X\u0096\f3\u00E8Eh^\x0B\u008F\u00BBpO\u00C7<0\u00C5rDt\u0093\u008A\u00B5\x1B\u00FD\u009A\u0096\u009D\u00E1TV\u008AF\u00EB\u00FA\u00D1\x1B\u00C3\u008E\x1A%\u00DA\u00CB\u00DE1\u00F6\x11}\u00EC\x16V@'tk^\u00EF\u0092\u00D7L\u00CF\u00C8\u00BF[\u00F2\u009E\u00B0\x12\u00D2\u00C0\u00BD\x0E\u00D5\u00A0k/\u00E1+Oi\u00E9\u00FE\u00A4\u00D5S,\u00B1~\u00CC\u00A7t\u00FDWC\u0086jT2T\u00C0\u00B1\u00A1\b\u0095\u0085{:\u008B\u00D9\x02\u00CB'\u00F6\u0097\u009D\ne@\u00C9;%\u00E3FUy\u00E1\u00E65\u00EF\u00FC\u00DD\u00F5\u00EDK;\u00BCu\u00D3J\u0083\x12\u00B7q\u00A8\u00C2^C\x04\u0083M\u00ECs\u009D\u0084\x0E\u008E\x15\u00F8\u00FF\x02\u0094\u00D1\u00D4\u00AE\u00F9\u00B0\u00FCyF\ff\u00BF\u0089\u00FEWaO\u00B7\u0098\x1A\x17H '\u0081\u00C3\u00E9q\u00CA\u00B1\x1D\u0081\u008E\u0090\u00A5W\u00AF\x00\u0088\u0082\u0096\u00F2\u00B2\u00FB\x04\u00F5W\u00CC;\f\\\u00EC\u0096\u0082\\S{Bk\u00DD\x00\u00C49L\u00B9\u00CC\x14\u0098\u00B9\x15\x0B\u00F2O'\u00F8\u0095)8\x16.\rE1\u0091hD\u008F!\u00AC\u009E\u0093~\u00C8\u00AB\u009F\x14;u?\x16\u0098J\u00B4{\u00ADA\x1ES\u0089\r~\x1D\x0E\u00D4\u00F2\x1A-\u00EF\u00EF\u00C0\u00A4\u00A7kE\u00C5\u0080H\x07k\u009B+\u00CC$Z\u00B99\u00F0l\u008B\u00C4\u00FD\x12\u00A8,\u00DBQ\u00A9\u00C7\u00B6Z\u008A\u009D\u00DC\u00A1\x1E\u008D\x02^{\x1D4\u0094\u00F3\u00D4\u00E7\u0081L\u00DBFS\u0099\u00EE\u00E7.\u00DB\x13\x0F2\x0FF\u00D33\u00E9E\u00E8;J\u00E1\x12&\u0085N\u00D5\u00E2OR\u008D\u0095\u00AE\\\u008F\u00E5\u0099\u0099\u009C\u0084\u0082\u00EE\u00FF6\x1Fy\u00F4]{~\x7F\u00EF\u00DF\u0097\x0Bo}f\u00A1\u008E[\u009Dv\\\x1D\u00E7\u0089\u00E8ol\x1Dy\u00F1\x15\u008B\x1B\u00FF\u00E2\x13k\u00C3:S\u0088\u00B6\u00B0ju\u00FB\x07\x00\u0080h.#\u00AF0\u00D5@g\u00D1\u00A7\u008AoO\x1C\u00A2\n:\u0085\u008E\x05E\u00F9P\u00D9\u0094\u00B4\u0096\u009BN\u00E20\u0086\x07\u00FER\x02W`\x16\u0081\u00AC\u00C3\u00AE\u0088\u00BAz\u0086'\u00AA\u00EE\x06Z\u00B6\u0081<\u0086\x04+5\u00D1`\u009F\u00AC$\u0093\u00992\x00\u00A6\u00EE\u009F`\x1C\x15G\x15X\u00EB\x14\u009C\u00A4\u00DB\u0092\u009CC\x04\u009A\u00CDE+\u00C9\u0088\x1D\u0098\u0082\u00F8\x12\x13S\x03l\x1D\u00CB\u00F7\u00CD\u00D3f\u00DA\u0081\u00A6\rc'o?\u008F\u00CB\u00A0|B\u00BD\u00F5\u00A0\u00D5h\u00C8\u00F9\\\x05VF\u00DD\u00BA\u00BD*\u0092\u0092\u00C2\u00EAy\u0098\x12\n\u009D\u008F\u00D6\u00FF\u00DF\u0095\u00E9L\x04-\u00BB\u00BE\u00E6\u00A8u\u00F00mB\u00C2\u00F2\u00FB\r\u00BAs\u0080\u00A8\x19\x12\u00A47h;\r\u00DE\x7F\u0093\u00D7\u00CC\u0095\u00A78?\u009A\x1B\u0081\u00BC\x07z\u00BD|\u0087I\u00B5S\u009FO\u009C\u0083q\x1C\u00DE;\u00FF^\u00F1\x0624-\u00C6\u00C1o\u0098i\u00E1\u00C8Z\u00F9|V\u0097\u00F7 G\t\u00B0\u00D8?n\u008F\u00DF,/>U\u00CA\x008E!\x03\u00C7s6\u00CE\u00BA\u00E2Y\u00D8\u00FBc\x1BK\x00\u008C\u0093\u00A4\u00F6_\u00B40\u00A2a@\u0093\u00A1\u0084\t\u0099u\u0098b\x06\u00E9a0\u00BE\u0095N\u00A2IZ\u00B5\u00DA\u00BB\u00A0E\u00A6#'9=\x01\u00EE\u0093\x15\u008D^f\u00EA\u00FB\u00D3\u00D2X|\u00D8\u00C0.:A&\x19\x052r-\u0086V\u00E2K\u0099Z\u00A5n\u0084\x17\u00B3\u00C6\x0E\u00A8\u00D1\u00B2\u00F07\u00EC\u00DA\x15\u0082-(\u00B6\u0094\x19\x07kz\u00BADv?\u00A2\x0B;]\u00E6\x1D\u0093,\x00\x01?\x02rK\u00B5\u0086\u00A9\x0B\u0094\u00EDK(\u00A0\u00B1\u009F$J\u00EF\u00F7\u00C9NL\x04\u00F9\u00CC\u00CDv\u00D7\x1D\u00D6\u00CBa\x02\u00DA\u00B9\x10N\u00D7\u009C\u00C8\u00AAN.\u00C2R\u00AF\u0093\u008CG-?]5\u00DC\u00FC\u00E8\u0084\u00E4 \u00AB}\u00EE l\x00\u00AA\x1Aa\u0084e!,|X\u00F6\x1E\u008DX\u00A7\u009D\u00A6\u00B2'\u00E5\u00B2#\u00C0\u00CC\u009A\u00E5\u00B0\u00BF\u00F3\u00D8\u00B9N&\u00AF7\u00DF\u00A7u\u00D4f\x18\"Xj\u0086Y\x15\u00E8\u00A6\u00FD\x02\x10^J0h\x1DxL%\u00E2Y3\x0F\x19\u00B8\u0092Y\u00E0\f~\u00E0\u008E\x11;\u008F\u00DE\u00FA\u00B1\u00FD?\u00BA\u00F7\u008AS$\u00BE6'O\u00E5\u00C1\x00\u00E0\u00AFT\u00E7/\u00D9\u00BE\u00E9\u00FD\u00EF\u009C/\x1E9\n[\u00B4W6\u00E1qx\x04\u00A5\x1E\u00C1:\u00EFL\u00AD;5ih~j\u00D4\x0Et\u00A4\u00A6\u00D5\x00\u00FDP,T\u00D9\u00DE\x7F\u00F7\u0089\x16w\u008D\x1CV\u00D4\u00DF\x1E\u00E9\u00B1(\u00C7u\u00E5a\u0096\u00CD&G\u00E4\u00AF\u00FD\u00FA\u008C\u0080\u00A38\u00A6\u00F6\u00C1'\u00C9j\u00ECh\x16@\u00B1\u00A1\u0082}#\u00B0\u00A1\u0082\r\u00F5>\t\u00DD~7\u00B5pa\u00EE\u00AE\u00EC\u008C.\u00AA[\u00C9\u00F9\u00D2\u009FfO\u00E0\u008B!\u0083\u00A8s\u00F2\u00B9\u00BD\u00BB\u00B2\u00D5\u00CB\u00A0G\x01\x15\fK\u008D\u00EB\u0095\u00B1\u00D4\x0F !\u00DE\x01F\u00B9f\u009F\x06\u0081_\x1B$\u00F8\x06\u00AB\u00F5\x0B\u00B6\x1F\u00C2Z[\x1F\u00C9\fI\u0092\u00DA\u00EC3B\x11\u00E7&\u00DD\x19J\u009EC\u0086\r\u0080fX\u00C1\u00D0G\u0093[ \u009D\u00E0\u00A6i.\u00D6J@\x05k\u00ACr\u00EDy\u00CD\u00E4.0\x1Ce\u00BDC\u00E5j\u00B4\u008Aa\u00F1\u0099\u00FB\u00F3\x1C\u00C8\u00ED\u00E0\u00F5\u00F5\u00E2u\u00A9=\u00D3I\u00DDC\u0084\x12\u00F6\u00BD\u008E9\u00DF'\u009E-\x10\u00F3\x11\u00D2\u00B1y\u00C9\u0091\x0F\u00EF\u00FE\u00CF{\x1E'\u00DF,\u008B\u00BFM\x1EOt\u009C\u00B2\u0090\u0081\u00E3\u00C1\"\u008B\u00FF\u00B2s\u00F4\u00F9W.\x0F\u00BE\u00F7\u00AA9\u00E6!\u00F8\u008A\u00B2b\u0092\u00BB\x7F@\u00FC_\u00948B*\u008Ax\x00\u00F1\u00920\x11\u00EC\x10*d,\t\u008F5k\x05c\u00C5\x02&\u00DDp\u00F8\x12\"\x04IA\x0E\"\x13?sO T\u00952\u008C\u0098^\x13\u00DB{GM<\u00EC\u00DC\u00E1\u00A9\u00F8i\u0098\u00BD\x10\u00FF~\u0084Yu\u0082T\u0083\u00DAa\u0097\x14\u00E2\u009ETa\u008C\u00D3x\u009Bno^O\u00D2\u0099\x19\u00AAD\x15`\u00B9\u00D6\u00DAX$Z\u0096\u00F1Z{y\u00FE\u00AA\x1E\x1AH\u0084\x04\u0093\u00D0\x05\u00D99it\u00E5A\u00A4^a\x169\\\u00D1nX\u00C3\u00E8\u00D7\x16\u00F5\x05\u00BE\x7F,\u00CE\x02\x0F\x05\u0090\u00EF^|Nt\u0098\u00C5\u009E9[\u009FF\u0082\u008A@\u00CA\u00BB\u00E1\u00EF\u00DE\u00F33\u00DE\u00D7\x18\u00F7\u0087l\x13\u00E7.:\u00B3.\u009D\u00DFu*\u00DD\x14V>'\x1A\x18q\u00EF\u008FF\u0088\u0082\u00DFz\u00EEW\rD\u0086\x0B\x1AaB8\u00FF+\u00CA \u00E6\x0E\x14[\u00E7\x1E]\u00C8S\u00DB\u00F3O\u00B52\x00Nq\u00C8\u00C0\u00F1\u00C2\u00B5\u00DD\x1F\u00FA\u0096\u00BE\u00EB\u0095k\u009D \u00A2\u00AD\u00B7\u00D0&\u00DEA\u00F2\x0B\u00AC\u00FD\x1A`@d\n\u00AE\x1C\u00EFg\u00CC\u00BFc\x02\u00BB&\x0E\x16\x19y\u00E3\u00AE$X\x0F_\u00B5\u00BC\u00F6\u00EE8\x02\u00BFg\u0098\u0080|\x11.(\x15\u00C0\"\u00FA|<r\u0093x\u00BC\u0099EG99\u00E9\u0081D\u00BF\u00C3:1F`\x1C\x15[\u00AA\u0081\u00ACGSVE!\t\u00E9\u00D4\x15W\u00C4\x12p\u00C7\u0086\x0E\u00C5\x05F\n\u0082\x03\u00FC6\x1F\u00B9=VHP\u009D\u00C74\u00D7\u00DE\u0090~\u00C5r\u00EC\u0091\u00E9\u00C0\b\u008CK\u00B8\u00EB\u00CF\u0090C3\x1Cpa:\u00F6\u0087\u00CDUK8R\u00CE_\u00FF\u00D6\bA\u009CX5\u0096\u00B0\u0084\u00C6\u00A0\u00FCp\t:\u0086CyO\u00AE\u00A0;\u0082W\u00C1%\u00E1j\x16\u0084Y\x18.\u00CB\u00C6F\u00B0I\u00C6\u00B2\u00D0e\x1Ar\u00E51T\u00F3\u00BE\u00EB\u00B3\x185\u00C3\x12\u00BE\x07\u00E5\x01\x148^F\u0082#\u008C\x15L\x19\u008C\u00B2\u00C0\u00D1\u00C7\x1Cz\u00E5\u00EE\u009F;\u00FB\u00CB\u00EE\u0093x\"\u00E3\u0094\u0087\f\x1C\x1FS\u009D\u00BDt\u00EB\u0096w\u00BF}\u00AD=A\u00A5Y\u00BE\x1A4\u00BC\u00B5\u00A3\x11\u0087c\u00D4\u009A\u00B9\u00DC\u00E8\x17Y\u00B4i\u00F0\u00CF\u00FDAR\u00E3\u00D6\u009A\x04`\u00DA\u00BB +\u00CD\x00\u00C0;\u00DE\u00B8\u00E57\u00FE:{\x05\x00\u00D1\u00F7\u00A0x\x0Bm\u00B2?\u00D2DN\u00BC\x04f)r\x7F\u00D5\u00A95\u00A1\u0092c\u0098\u00A0Tf<?\x04\u00FB:\u008C\u00CA\u00EC\u00A1\u00C3z\u00F7\u00B6j\u00DD\\t\x16\u00FCX\u008B2\rD\x7F6V\x1E\u0082b\u00D6\u00B3\u008AO\u00D4B\u008E\u00D6\u00DDj\u00BA\x0B>_\u00DA\u00FDR\u00A1\u00B6\x0E\u00B4\u00A5\u0086%&\u008D\u0099\u00DC\x03\x16[\u00D1\u009D\u009F\u00A9`\u0090\u0092\u00F9@\u00F2\x02\u0086\u00F2w4x\u0081_3\u00B3\x11\u00B4\u00BA\u009A\u00D9\u0083\b\x1DT\u0093\u009E\f\u00CD,\u008A\u00B6\u00B0\u00A2k\u00A3=K\x16R!0\u00A0\u00F4\u0098\u00E2\u00FF=\u00C9a\u00E8\x02\u0095\u00CC\u00D0\u00D8sS\x10\u009C\x1E\u00BA\u0084\u00EB\x0F\x14O(\u008E\u0085r\u00ACr\u00BE\u008E\u00C825\x05\u00DA\u0092\u00F3/\u0097\u0096\u009B4\u00A8!\u0088\u00DDK\x18\u00E1\u008A\u0085\x18B\u00E5\x1Btt\x1Cy\u00C8\u00E1\u00F7\u00EE\u00F9\u00A5\u00BDO\u0092\u00AF\u0095\u00E5\t\u0088\u00E3\u0097=Ny\u00C8\u00C0\u00F1P\u0091\u00E5\x1B\u00FA\u00E2y\u009FYn~\u00F0\u00CA\u00B9\u009E\u009D\u00CEv\u00A5*#?#xR\u0084\x7F\"\u00EC>y\u00BA:\u00D3\u008B\u009AT\u00B2\x12\u00CE\u00F6\u00F1\u00E3\u00D6*\u00B4\x0E\u0098\u0092\u00D0xA\x14\u00C2\u00EE\u00BF\x03\x04*iC\u00BAp\u0091)p\x173\u00B2\x18n\r\u00A8\fX\u008D\x06\u0094}9\u00C9c;qE\u00E3\u00E7u\u009FX}\u00E2o\u00BA\u00E0\x0Fn\u0099\x07\x17\u00D8@\u00C4{*(\u00F1,\b\u0095^\u0084\t]\u0082\u00DA\x1B\n\u00B1kL\u00DAT\u00AEN\u00CEr\u00E5A\x0B\x16\u009F\u00F93\u00ECJw\u00D5\u00AC\u00B0\x02V\u00B8\u00EA\u00F7\u00DCk\x00\u00D83t\b\f\x07)\u00F8\u00D1\u0097B\\A!\u0089`\u0082L5*\u00A4\u0084,F\u0081\u00B6\x10\u00C2\u00F1\x1Ca\u00F6$[\u00A13\x05\u00D8\u00E9\u00F9i\u00A58O\u00E7\t\u00C3\x01zk\u00DC\u00C7\x04\u00DD\u00F8\x06@n\u00CBP\u0086\u00EB<D\u00F8\u00EA\u00CF\u00BD\u00C7v\u0099\u0091\u00EA\u00E1\x1D\u0092\u0091\u00C8\u00FBp/\u00C4\u00B1\x14z\u008D\u009D\u00CFbr\u009D\u00AE,\x04\u00D8\u00BC`\u00E7p\u00FB\u00B6\u00D9\u00F3n+e\x00\u00DCF!\x03\u00C7\u00F3\u00DA\u00FC\u0093\u00FFP\u00DBw\u00EF]v\u00A86t\u00E7 d\u00C5\u00A2)\x07f\x00\b\u00BCU\u00D2\u0091\u00BDD\u00CD\x17\u00A0\x12\x02\r\u00D0\r/\u00F1\u00BA\x02Q@\u00E2\u009E@-@\u00AA\u00AE\u00BA\u00F6\x16\x02$t\u00F5\u00FD\u00A7\u00E6\u00E4\x03{\u0088\u00FD`\u00EB\x10\x04XG7S\u00F2\u00FA\u0098\u00B9(\u00FB\u00D52\u00EC\u00F0S\u00C1\u00FB\x15`\x14\u00EC\u00A8`\u00A7{\u00D8\u00E0!DE\u00C6\x17=\u00DD\u00EF$\x05\u00C1;9\u00BBP\u00BB\u00EBonrG\u00AC\x7F\u00D03\u00E41\u0097:q\u0096\u008Et\u008F{\u00D9\u0096n\u00EF\u00B2\u00AB\u00AD)\u00E1J\u00C3B\x17\u008D\u00CCE\u00D4\x1Dhn\u00C3\u00DB\f\u00C5\u00A1\u00B9\r3\b\u00B9\u008C\u009CN~/\u00BBb\u00A1\x1A\u0099\u0083\\\r;\u00D7\u00A5X\u00D6c)\u008E\u00C9\u0098L\u00B2\x03\f\u0085\u00F8\u00CC\u00EA\u00F5\u00F6\u00BCft\t\"W\r)\u00F8\x7F\x1A\u0081\u00B1\u00DCk\u00BE\u008FZ\u008B\"\u00E55K\t\x192\x1C\u0089\u00F7\u00E1\u00D3 C\u00CCcA\u00C5\u00ED\u00F5m\u00E0\u00EF-\u00BE\u00FB\u00EC\x1F>\u00EB\u0093\u00A7L@\u008F3n\u00B3\u0090\u00A1\u008E\u00E7\u00EE\x1C~\u00C3o\r\u00F2\x1D\x0BY\x07 \x13\u008A@\u00E4\u00A7)<@\u00B8O\u00B5\u00F5y\x05\x05iU\u00A8\u00E5\u00CD\x05v\u00F7\u00BF\u00A2\u00B6.p\u00E6v\u00B5\u00CCR\u00C4\x04(\x00\u00A2z>\u009CV\u00A9\u00E7\u00DFD\u00E0#\u00DB\u00E0\u0096(\x1A\u009A\x14\u00AB\x1B\u00C0\x10\x15\u0089j\u00B8\u00B3\u00DA}\u00A5g\u00B0\\\u009Ai,\u00C0L\u009EY\u00B15U\u009C=\x02\x1B\n\u00EB\u00AA\u00D4\u00D3\u00BD\u009DuC\u00F4\x07\x18]\u00D9:+%uwp\x0F\u00A3uX\x7FD\u00BA\u00C1c>\u00C7\u00EC`\u00ACAUV\u009F\u00D9m\x14\f\u008E\u00A5p-D\u00D1\u00CC,\u00CC\x15IM\u0086F\x06\u0082\u00FD\x14\u008D\u00ACna\u00C1\u008C\u00E7\u0081\u00B9\u00E4\u00E1\u00C2{\x16imi/7\u00C3\x04\u00C1L\u00D3\u00927d\u0086 i\u00C2\x12\f\u00D4\x19\u00AD(\x18\u008E\u00AC\x10\u008B:\u00C3\u0081\u00E9\u00F9\u00E9\u00C50\u00A5<\u00F4\u009CO\u00B1\u0096Ex\\\u00C9\u00E7\x10d\x17%\u0086\u00B4\u00ADO)\u00CF|\x07@\u0086\x12\u00B5B2B\u00B7\u009E-\u00F3\u00F8\u008Cj\u0088R\u00A9\u00CB\u00A3(\x0E\x7F\u00ED\u00E17\u009E\u00F3\u00EE\u00BD\u00CF;!\u00C1\u00BB\x15\u00E36\x0B\x19\u00EA\u00F8W\u00F3\u00B3^\u00FC\u00D9\u00A3G\x1E\u00FB\u009E\u00F5\u00FE \x1Df\u00E1~\u00CAD \u00FD\x01\x14\u0081\u008A%\u00D1\u00FDe\u00A8\u00C9}\u0080v\u00DD_\u00BC\u00C2\u00E22z\x17&l\x1D\x19&4\x10\u00F5\u00B6\u00EF\u008B;\u00C8\u00F3\x15mL\u00AB\u0096D\u0093\u0095\u00EB\u00F4c\u00F0\u00DA\x18J\u00A4G\u00A0yl^\x7F\u00B7\u00C9l\nM\u008A\u00D2h\x06r\u0096s,\x14\u00D8D\u00C7L\x1B\u0096\u009C\u0098>\u0081:\u00CC\u00FAL\x14O\u00F9;p\nZ\x1E\u008F_\u00B53\u00BE6\u00EB\u00D8 \u00C17\u00E8\u00BCV\u00FE\u00BF\x1C\u0087\u00E4,q%\u00BC\x14\u00AF\u00C3\x00B0\u00FDQ\x068I\u00AF\u008EY\u0099\x16\u008A\x02\t\u00AA\u00C1\u00EEk\u00E6\x19'\u00C0\u00DE\u00E7RS\u00F0\u00C5-.\u00DB\u00A5Y\u00C8 \x11>VRQ5\x0E\x04\x7F\u00C5\u00CF\u00A3\u00FE\u00BE\x18\u00C2\u0091u)\u009D\u00DEN*\u0081t\u00D5\u00B3n\u0085\u0099\u0086\u00C8\u00C8h\u00F1|4\u0085\u009D^%\u00C3\x07Z\u00FFZc\u00D1\x03[r\x03\x03\u00F8j\u00E0\x00$C&\u009B\u00E8@oKl\u00DE\x7F\u00F3\u008A}\u00FFr\u00CF\u008B\u00F1\u00EE[#}'6n\x17\u0085\u00F0u\"\u0087\x7Fe\u00B1s\u00D9\u00F5\u00CB\u00F1\u00CF\u00FEZfg\u008D\u00E4\x19\u00C4\x0BP\u00B0\u00F5\u0098\x14\u00CD\u00C8\u0087\x1E3\u00A6hb0\u0096\u00F4\u0097\x17}\b\b\u00EE\u0085\u00DB\u00E5\x0F\u009F/'\x00I\u00A4\u00A7@\u0081\x02\u0092J\x1A\u00E7\u00D7\x10\n\u00F8$\u0099\u0080\u008BeB&\b\u0084\u00D8?\u00D9\x7F~\x1BJ\u00EFG\u00E2>\u00AA\u00A2\u00A1\x17\u00B4\u00A3\u0082-L\u00EB\u00FE[U\x1C\u00DA]\u00C1(\u00E0\u008D9\x06\u00BF\u009F\u00A5\u00D2\u009B\u0090P6\u00E1\u00C6\x17w\u0094q,\u00C3%\u0082\u00AA|\u008E=\u00C22o\u0094\u0082d\u00E9Q \x15\t\x1A\u00F2s\n.i\u00C2\u00E6r'\u00AD\u0099\x19\u0082\u00DE\u0081\u00D11\u00A0\u008A\x1B\u0090?\x10\u0082\u0085\x14n\nce\x05N\x18\u0081\u00E4\u0086\u00F0\u00F9#\u00BDB\n}\x02\u00C79\x07\u00EA1L\u00A1I\u0080\u00B044\u00AD\\;\u0085\u009Fn\x7F\u00E0AZK\u00E0\u00D3\u009B\u0088g\u00DE\u0099X\u00D7\u00C8@\u0091UY\u00E7\u00BB\u00DD\u00ABb\u00FB\u00DC\u00C5\u0091\u00E5S\x16\u0097\u00C9s\u00E5\u00F0\u00DF,a\u00A7n\u00DC.!\x03\u00C7\x0Fnm?\u00E7u\u00D2\x7F\u00FD\u00C6\u00F9FX\u00B5\u00C0\f\\\u00F8d\u00B4K\u009A(\u0083\u00D5\x1F`j\x195_\\Xh\x07\x07M\u00D8[\x1E\x0FSl\x00e\x7F\u0081B\u00DDm\u00A7\u00F5\bBIy\u00A1q}\u00E58i\u00A1\u008BBb&\u00A2O\u00CF3Qvq\x1F\u00EA\x12F\x0Bh\u00C4\u0099\u00B3\x15\u00D8\u00A5\u008A\u008D\u00D1\u009B\u00AC\u00AA-\f;S\u0089\u00D0 \u00C2\u00841\u00D1\u00FA\u00E6U\u008E\u008D\u00D5t>\u00F9\x06'1\x11\u009C\u00D4n\u00A1E\x00\u008C\u00DA\u00CC\u009A\u0095n@<\u00AE\u00887\u0080\u00D5\u00CC:\b\u009C(\x05\u00ADkrg}\x054\u00DB\u00B1\u00FBs\u008A\u0090E\u0081Ah\u00B1\x11\u00A1C\u00A0\u00FFE\u00A0'\u00D5\u008E\u00E5\u00F8\u00F1\u00B9\u0083y<6\u00CA\u00FE\f%\u00F8^\u00E9\u00FD\u00CDz\u00BE\x076\u009A\u0099Tb\u008Ei,\u009A{}\fE\x18:@\u00E9=tW\x12-\u00CE3x\u0096LF\u0086oTh\x06(G\u0095\u00AAf\u00D8C\x1EK\u0083b\u00B9k\u0081\u009D\u00A7\x1F}\u00EE\u00BE\u00DF<\u00F77n\u00BD\u00D4\u009D\u00D8\u00B8]\x15\x02\x00<\x7F\u00FB\u00C8\u00CF\u00FE:\u00D6\u00BEw\u00C4,d\u0086\x16i\u00E2~\x13\u00A1\x05Bp&\u00CB\u0083\x15aLP\x06\u0091t'\u00BAkB\u00DC\u008A@j\u00E0\x11t\x0B\u00AD-\u0098\u009F\u00D3\u0089\u00FA6\x11<\u00EE\x0F\r\u00EE\u00A9BZ\u0098\u00E3\b|]\u00E3\u0081\x16\u00825\u00FF\u00C7x:\x1E\u009F*\u0089\x02\x1DY2\u00EB\x13\u00DE\u00D6oP[\u00C3A\u00815\u00C7\x00\u00E6H\u00C1\x1F\u00D4\u00D2r\u00C3\b\u00B0\x0BsS\u00EF\u00BAT\u0094&\u00D4R\u008D\u0099\u00A5\u00F0\u00F3,M!\u00D4\u00C5C\u0086\u0091\x1E\u0083F\u00B8bBo1|\u0093d\x1E\u00CE\u00BA\u00E1\n\r\u00885\x17\x06%k1k4\x04H\\\u0081\u00CA\x01S\u00C1W\u00E4\u00CAUA`\u00D7\u00F4\b\u00A2;SG4\u00A6\u00AD\x05R\u00A1H4\u0085\u00BBi.e\x17)Cn\u00E7\u00F3mV\u00B8+\u00C1:,\u00EFp\u00D2]\u00C9\u009F\u00B5\"=\x05\u00D1\x1E\u00CF\u009E\u00F8R%\u0083\u00D5\u00B4%\rY\u00AClU\u0094W\u00CC\u00F96b\u00F3\u00D2\u00A3\u00AF\u00DA\u00FFG{\u00FF\u00DD\t\t\u00D8I\u008E\u00DB%d\u00A8\u00E3%k\u00BB_v\u00DD\u00D1\u00AD\u0087\u00BD}\u00C07J\u00B7\u00D3'\x1BP\x01\u00C6\u00D7!p\u0098\u00B8\u0086\x15\u00B5N\u00E5\x01D6!\x18a\u00C7\u00D2\u0098\u00A3\x1E=\u008E\u00EFB^\u00D2C\u00D9\tI\u00D3\u00F2\u00C3<\x03U\u0098\u00C0\n\u00DD\u00C5\u008C\u00FB'\u00E9\u00A5r\u008DUq\u00F0\u00B0qo\u00DC?\u00E8\u00AA\\\u00C6\u00DB\u008E?*\u00B0\u00A5\u00E6-4\u00A4\x0B-=Kn-\u00A4\x10g\u00C9\u00E9\x04\u008Cb\u00E8P'!3\x05\u00E1\u00CAr\u00E1\x11\u00AF\u00924\u00CCAb\u00DD\u00C4\u00C0\x12\u00EC\u00AA\x02=wq0\u00D7\u00B6\u00E7k`\u00EB{\x03N1q\u00DF\u009B8k\u00D1\u00DFE\u008D\u00FF\x13W\x02\x14\x12\u00E4\u00A9\u00DA\u00E0&R\u00BB\f\u00954\u00D7\u0086\u00ACJ/\x04]3L\u00E0\x1C\u00C1X\u00D6\u008EPs\u00F7!EI\u00F2Zy\u00FF\u00FE\u009B\u0085i\u008A\u00A2\u00DC\u008B\u00B0\x0F\u00DA\u00B28\u00AB\\3\x15}4\u0084\u00F5w\u00D3\u00D4l\x17\u00D9\u0098\x13\u0082\u00BF(\u00B6\x1F\u00BE\u00F3\u008Es^\u00B5\u00E7ex\u00C4\u00DF)R\u00A7t\u00DC\u00EE\n\u00E1Q\"\u00E3\u00DBT\u009F}\u00F3\u00E1\u00CD\u00FF\u00FB\u0081\u00D6.\x01$\x1E\x12 \u00FE\u00F0s\u00A5\u0087hP\u00C2\x17\u00EFnX\u00C4\u00DD\x10D\x1FEmeR\u00C81\x02\x1A\u00AC\u00C1\u00F0,\u00F2\u00E50\u00A5\u00A9\u00F5E\u00FB66\u00D15C\x04\u009E\u00DA5<\u00CA\u00FE\u00D3x\u00B6\u00A4<y\u00FD\x1EVT:sN\u009E\x04\u00BAx\x1D\u008B\u00B1cP\u00C1\\\r\x0FX\u00F85\u00AF\u00F1\u00FE+8V\u00EE1Ro\u009A\u00CDGxL\u00F2\nH\u00A9%\u00B8%\u00CE\u00D8\u00E4\u00C4fZ\u008D(x\u008F\u00DF\u00EA\u009C\x01\u0085\u008A\u00E3\x0B\u00DA-\u00CF\u00E3\n,dA\u00DD\x13So\u00E7\u00EE\u00CF\u00A6Y\u0080\x16B\u00AF\u00AE\x11\u00D8<\u0095\u00D9\x19>\x0B\u0086\n\u00CB\u009Ea\u0081\u00A5\x05{\u009C\u0093\u0095\u00A2y\u00BF%\u00C3\u00E3/\u008D^\b\u00A7E\u00BC\u00BF\u00FA\x1E\u00FC\u00DE\u00C7\u00E2\u00E1q\x15(f\u00B4\u0080\u00F4`b>h\u00C1\x18\x14\u00A1\u009C[7\u00E6\u00A7\u0091\u00E0\u00FC\x19z\u00A6F\u00CBo\x00\u00E8}\u00C4\u00E2+\x0F_\u00B9\u00EFE\u00BB\u009F-\u008F\u00B8u\u00AD\u00D4Of\u00DC\u00EE\n\x01\x00\u009E&r\u00F3\u00AB\x17\u00DB\u00CF8tt\u00FB\u00CF>\u00D66\x0E\u00F0%\u0085\u00E0\u00A2Z\u00F6\u00D2\u0085\u0086\x13$\u00B6\u00D5\u00B0\u00C4\u00B4x\u00B5 )\u00ADr\x11\u00C0\x12f\u00C4q'\x1A_CYh9G\u0086\x19+\u00D6\u00C8\x15\x05_\u00F6$\x05\x19\u00D7\u00AFay\u00C8w\u00AF\x13PBy \t4\x11\x12\t\u0096\u00A3b\u00E1\x13)]\u00DE\u00AC\u00C3\u00E7\u00FA\x0B\u00A1\u00D8\u00FA\u00D4%>\u00E6\x1E\u00A9\u0098\u00A2\x17\u0080\x04:\x1F\x18\u0088\x13\u0093\b4Z\u008D\u0082#\u00EE*\u00DE\u00EC\u00C7\u0098\u008A\u00BD\x03\u00A1\u00F0\nV0bZ\u008A\u00DEt\u00F5=\u00DA\u00E7,~\u00A2\u00D5\u00B6\u00F7;}\u00DEI\u00FCq\u0090\u00D1\u00FF\u008E\u008CMy\u00E7\u00E4<4'-Qqg\u00EF\x0BW\u0088@d<\u0096\u009A \x1FT\x03\u0093H\x0F\u0086\x041;\u0099\u00FAs\x1F\u00E8\u0089N\u00DE\u00A9\u00960\u00C1\u008D\x12\x14u\u00DE\u00F1\u00B8\u00EE\u00A0\u00D8\u00B9\u00A4cy\u00AF\u00AD\u009B\u00E4\u0099\u00B3g\f/^\u00BB\u00F9\u00C4%\u00EB\u00E4\u00C7\x1D\u00A2\x10\x00\u00E0%\u00F3\u00F5O\u00FD\u00A7\u00AD\u009Do=\u00BC\u00B9\u00FC\u00C3\u00AB\u0086\u00D9F\x15\u009E\u00AC\t\u0097I\x18\u00C0\u0087m\u0093\u008FT\u00E3d~U%\u00C1c\x01\u00B0\t\u00E1\u009A=\x15\u0086\u00BB\u00C8\x13\u00C4\u00B9f\b4\u00CEy\u00BC\u00E2$(_6{(\u00F8\u00A9\x03\u0084\u00AC\u0093D&\u00C4\u00A6\u008ADW`r\u00F2\u00D3\u00F3X]\u009B)\x04\u00B5\u00DC\u00FF\u008E\x0Bt\u00E3*K\u00CA\"\u00A8\u00E3\x1C\u0097n/\x05h\u00AC\u00CA\u0082J\u00A0\u00D6kHxC\u00D1\u00ECe4\u00C1\u00E3\u00E3\u00A5@\u00AA\"V\u00C3c\u00A8\u00C7\u00EF\u00CC\u00BF\u00CB\f\u0082\u00F8kc\u00F8Q\u00F1\u00A1d$\u00A6\x17\x12\u0082\x03D\u00E8U?\u00D7r\x1E\x14%BA\u00E5\u00FD\u00A8\x7F\u00CF\u00D0#\u00C2.Me\u00AC\u00D0\u00C8\x14X\u00EC\u00EF}%}\u00DE\r\u00AE4l.\u00E5<\x0BP\u00B1\bx\u00ADUi\u00E5\u00BD\u00B7\be\u00F3\x19\u00D9\u0083H\x0Fa{\u00EF\u00E6\u00D6\u00DAS\u0096\u00DF\u00BA\u00FF\u00E7\u00CF\u00F9\u00D4\u0097#C\u00B7\u00C5\u00B8\u00DDA\u00C5\u00D5\u00F1\u00FD\u0087\u00B6.\u00FB\u00E5\u00A5\u00BC\u00E9\u00A0\u00CCm\u009D\u00D1\u00D1\\\u00C9I\x1F\u0082x\u00F1\u009A\x02\u00DD;Zw\u00A7\u00AD\u0082|\u00A5\u0096`\u00D5\u00AA\x0B\u00DC\u009A\u0085\u00D0\u0090R\u00AA\u00A1\x1CZ)~\u00A1\u00D5;f\u00B9-N\x06W2\u00ADX%\u009D\x1C\u00FF\u00D8\u00BF'\u00BF;\u008E\u00F1(2\u00BB\u0091\u00F1\u00A8\u00AA-\u00CD\u00BE\u00A6\u00C0n\x15\u00CC\u00D5\u0097\x7F\x1B\u00B9X\u008B\u00F74\u00EC$)e1W\x05\u00C2*:.\u008A\u00E0\u00DA\u00B3i\u008A\u00A8\u0081\u0094\u00EA\u00CAbX\u00A6\u0095c91\x17ba[\\\x02\u0085\\\u00C8e\x10\u0099\u00D46\u0088f\u00BBvZ\u00E5\u0086\\\u008A\u008E\u00E0\x1AC\x02\u00DBOJ\u00A9\u00F4\nP\u00E8\u008AAx\u00DD\u00FCL\u00E9A\u00D1\u00DBI\u00F4>j\x19\u00C0X\u00BE\u0086\u008A\x1A\u0099\u008D\n(\u00D6mf|\x0F\u00DD\u00B2;Tj\u00CC\u00D8X&\u00C6\u0095\u00DF\u0098\x00&\x17\u0096\u00D5r\u008D\u00F5>\x04\u0096e\u0081*\x16g\u00ED\u00F4\u00E1\x1F,\u009E}\u00D1\u00E5{.?I\u0091:\u00A9q\u0087+\x04\x00x\u00E1\u0097\u00B6\u00BF\u00FB\u00F2e{\u00F5!\f\u00E9\t\u00C0\u0080.s\u00B3\u00BD\u00E0\u0086BJ\u00A1\u00EC\x00\u00AAu\u00F6mk'\u00A5t\u00913\u00F4\u0098\u0084\x02L\r\u00ADz%\u008AH1\u00D6\u00F4&\u00B1\u0081\x10\u00F4\u00C2YXm\u00B0\x1Ay\u00FD*\u00F4+\u00C7Ze9\u00C66=\u00F3\u00DA4Om\u00EC\u00D8\u00E8\u0082]\x00\u00D6\u00BB`}\u00CC\u0095\u00A2C!t\x16>\u00D5\u00F0\x02\u0099\u0092\x1CY\u0091\u0089\u00F81\u00A5\u0090?P@GE[d\u00A3R\u00AE\u00CD\x10\u00A4$\u0098\u00A0\u00CF\u00D0r\u00B2\u00C3W\u008Bv\u0090\u00CEX\u0089\u00DE\x10\u0096\n\u00C1\u00B7#\x0BQ\u00B4d\x1E\u00E0\x19\x12\u0094^\x04(\b\u00BF\u0094\x15\u00AE\u00B9?\u00AC\x1E\u0080\u00AC\u00C2X\x03\u0092\x1E\x0F\x18\x16\u00A5r\x10?\x16\u0095\n\u00C0\u00E7u,\u0087\u0080d.\u00BE\u00F3\x19\u008B\u00AB\u00D4\u00FB4\u0094U\u00B7\u00C9n\u00E4\u00DFdM\u0082\u00D7\x00\u00DEg^[\u0083\u00A0\u00AF\u00ED\x00\u0097\x1E}\u00C9\u00BD\u00DE\u00B6\u00FF5\u00A7B\u009ENf\u009C\x16\n\x01\x00^t\u00CB\u00E6\u008F\u00BC\u00E1\u00A8\u00FC\u00C7\x1D\u0099\x03hnm]h]\u00E3G\u00E5\x17\x1F\u00B43\u00C9\u00C2\u00E5W$\u00E8X\x05?\u00D2i\u009A\x02\x16\u00F1&\x17T\u00B1s\x19kM#\u00ED\x18 b(!)\u00D7\x04\x18\u0093\u00AD\x1C\u00D7'\u0098)\u00A6\u00964l*\x11fS\u00FC\u00D8t\u008B\u00E9E\u00E4\u00F2]~\x7F\x00\u00D8[AF[\u00EAm\u0097\n6\u00C4R\u0090\u00EBN\u0085\u00AD\u009E\x02\u00C3\x03\u00AB\u0086\u0094\u00E8\u00B3h\u00FC\x02\x13\u009A\u00A1TG\u00D2#hK$\u00FBpt\x0FA\u0081X\x17\x11\u00DE=\u00D9\u0085w\u00A6\u00E6\x11\u0090\x1A\x1C\u00A9G\u0094&&\u00C8\u00CA\u00C7\x14bL<\u0084\u00B0\u00FA\u00FC\u00BC\u00E7\u00B6L\u00F9\u00F2\u00BBJC\u00B6\u00FD\u00F3\\\u00D0\u00E9\u00F1\u00AB+?I7\u00F6\u0082\u00E8\u008F+\u00DE\x06\u0089CE\u00B0M\u00C8\u00ED\u009D5\u008F\u0093\x04l\u009E\u0092\u00FD\x17\u00E3<\u009DJ\u00C8\x19\u009E\u00C8P\u00C9\u00B2\b\x12\u00F7\u00A0\u00C3\b\u00BDt\u00F3G/~\u00C7\u00BEW\u009C\x021:\u00E9q\u0087a\b\u00AB\u00E3\u00C7\u00F7l\u00BC\u00E2\u00D0\u00CE\u00E1}o\u00DEi/YHKk\t\x14a\u00AB(~\u0092\u008D\"\u00C5\u00E4\u00FFW\u00D4\u00CF+O@\u0082\u00F1\x16e\u00BF!\u00F4\x068\x11\u0095\u00AE\u00E0cZ\u008C\u00FC\u00DC\u0080.\x05J\u0096\u0084\n\u00C2\u00BC\u0085\u00CCC\x13S\u00C8\u00D0A\u00F2:QB\u0083h\u00B2\u0099%\u00AFZ\u00D8\u0089\u00B6\x00\n\u00B0\u00ADe;\u00AFu0\u00DE\u008B\x1D18\u00FE%nm^*\u00CC~\u008A\x1D)X\u00E2iO\u00A3\u0082k2\u00F4 N\u00DE\u00E9\x01\u00C4\x01\x1E\x1B\u00FB3dzp\x1E\u008AQ!\u00AE_\u0089\u0098\x11$n\u00AE\u0088#{\x00\u00A0\u00C9\u00B4\u0084=>\u00E7\u00ABG\u0089\u00FF\u0081\u00D8\u00D6\u0096Z\u0093\u00C9<!\x01\u0096\u008B\u00C8\u008E\u00E5\u00DD\x05\u00B0\u00D8\u00ED\u0099\x1A\u00CC'\u0091\u0089`\u00DF\u00C9\u00CA\u0094\u00ED\u00A1\u0098\u00E1\u0098\x05\u0095t-\u00A5\u00F6K\u0098xZ\u00CC\u008C\x15\u00E5\u0094\u00CE\u00A6\u009F[\u00D1\u0087\x1D\u00B4'\u00ED\u00BC\u00FA>\u00AF\u00DF\u00F7\n\\\u00F4\u00E5H\u00C9m?N\x1B\u0085p\u009E\b>\u00AD\u00FA=;7n\u009D\u00F5;\u00DB\u00ED\x05#Z\bRX]\n\u00BC\u00CF\u008C\u0089;\u00DEsB1\u00EE>&\u00B3\u0080\u00F4\x1A\u0098R\u00C4X?\u0087\u0083K\u00C7Q2=\x15\r4\x17\x04\u00A9^G\u00E0\x18\u009A.y\x02\u0096\u00B9\x7F%)\u00D5\u00ED\u00ECo\x07HyM\u00BC\u00EF\x0E`4%\u00B4tr\x14\u00BB.\u008F\u008E\u00EC\t\x10\u0098K\u00F3\u0090c\u00E8Rp\r\x16\x0E\u0089O|\u0086\x04\u00F4V$B\"[\u008CE\x03\u00EB\u00B0\u0094\u00A2\u00ED\u00CF&\u00ABK8\x1E\u00E0\u00BC\u0081P\u00A6\u00C8\u00F6e$\x18(\x15\u0096\u00AA\x0B\u0097\x1Dz\x06\n^\n\x0F\u0095\x11\u0090\u00EF\u008E\u00E0!\u008Fk\u00AE\u00BD)N\u00AB\u00F0\u00B40 C1\r\u0081\x1CF\u00D8\\(\u00C7Su\u00E2TyG\u00CD\u00EB8\u00F8^\u009A\x1B\n\x05\"$\u00B1\x05]\u00EDb\u0082\u00A7\x10\u00FB\u00C3\u00B1$g\"\u00A24^u\u00D0U\x01H\x13`Xb\u00FE\u00D8\u00E5\u00AF\u00DC\u00FB\u00F5\u00FB\u00BEG.:m\x1C\u00F5\u00D3G!\x00\u00C0\u00FDD\u00F4\x13\u00AA/\u00C2\u00F5\u009B\u00B3\u00B7\u00EC\u00AC\u00FD\u00B3Q\u0087\u0089P\u00860u\u009F0\x13eP8\x07\x00&H>\u0090\u0093\u008A\u00E9\u00BD\u00C2O\u00E0\u00F7S \u00CF\u00AD\x050\u00C9\x04hy\u00C1\x14\u0094p\u00FD;\u00A2b1\u00EA\x17\u00A8m\"\u00FCI\u00A6\u00A3\u00E1\x04\u00D3\u00FB`\u00B8`\u00BB\u0099\u00B0NR\u0095]\u00C2\u009A-4\u009B\u0090p1\x17\u00E6\u00BCSY\u00B2\u00CE\u00C3\u00EEw\u00E6\u00A5\u00C49y\x19#'\u0088\u00DB=\u00AC!\u00EE\u00C24\x1A\u00DB\u00A7\u00C5\u00F4us\u00DEDaEGt\r\x10\u00D9\x18\u00EA\u00C8 \u0091\x05W\u0081\u00E8\u00BD\x1D-:L\u00F3=#\u00AD\u00BC\u00BA\u00F0\u00C7\u00F9\x19\u00EA\u00F8{k\u00AE\u00EC\u0098\u008D\u0088\u00E5\u00E74\u0081F\u00B6@\u00B3y\u0090\\\x0BhR\u00AD\u00BB\u00A6W\x11s\x00\u00A9\u00B4\r_\u00EA!\u00D8\u00AD\x18+\u00DE{\x16|\u00D1\u008B\u00F2g\x03x\u0093 {\x18k\u008F\u00DA\u00FE\u00B5{\u00FF\u00CC\u00DE\x17\u00C9W\u00C4\f=-\u00C6i\u00A5\x10\x00\u00E0\u0081\"\u00FDJ\u00D5\x17,\u00AF;\u0082?\u00D8\u00DA\u00F8g\u008B>L,\u00FCd\u00B1\u00CB\u0089\x15\u00972\u00F9\u00D3\u00BD\u009B\u00E0\x0B+J\u00A3\u00BA\u00AA5\u00E7\u009C_f\u0098\u0090\u00C7\u00D1\u00DC\u00AE\u00B8\u00E4\u00D5KY\u00BD\u00B6\u00C95\x04\u00FB\u00D2\u00FB'\x14\u0092R\u00E0\x14\u00A1\u0090|?\u00BA\u00FBE\u00B8G\x05\u00B6\u00DD\x1BI\u00A5S\x14\x1A-\u00BB[|z\x19\u00F4\u0080jX\u00D3\u00BBD\u00F6\u0081\u009E\u0085\u00B8\u00E2\x19\x01_,\u00D6n7\n|\u00F8l@\\6\u009F\u0097\x02\x18]pjHe>\u00BD\u00DD?\u008B\u009CF ,wM\u00E3\u00C5\u00A3.\u009Fu \u0084\u00D1X\u009A\u00C5\u0093\u00A2\x07\u00E6\u00F7\u00DD\u00F8\x1DX\u0081\u00E9\u00D6\u009A\u009EB=n}\x7F\u00F1\u00DE40\x1C\u00E2\x0E\u00E9\u00B1\u009AR\u0089\u00CAN\u00BF\u00F6\u00DE\u00F9\u009D\u008D\x0EWr>UZ\x17,gK\u00CC\x1E{\u00F4\u00D7\u00BE\u00EAU\u00FB^ _\u0093\x1C\u00D0\u00D3e\u009Cv\n\x01\x00.\x11\x19\u00FFJ\u00F5\u009F\u00CF\u00AE9\u00B2|\u00EB\u00D1\u008D\x17,\x1C\u009C\u009B\u00F4\u009E+\x13(x\x0B\u008A\u00A8E\u0088\u00859B\u00D0r\u00FF*\u00A0\f=\x18&\x103\u00E0[\u00D4\u0095s\u00C5Lu+\x1C` \u0090@ \x05\u00CEc\u00D5\u0089\u00F5\u00E7D\u009Bx\f\u0088I\x18\x1Dr\x14e\u00D5\u00E8\u00B4fp\u00D0\u00B3wk\u00FB\u00CD%\u00CB\u0088\u0092s\u009DG\u00A6\x1C\u0087\u0091\x16\u00DA\u00CEC\u00F6\x1D\u00A0\u008E5H\u00C6\u00D8\u008APR\u00B4\u0088\x00\u00A2\u009E\u00BFAc\x01Y*h+k\u00EE\u00FE\x7Fs\u0095\x15F)\x1E;\u0082\u0081G\u00EC\u00C6\x18zL\u00ED\u009A\u008E`\u00980*\u00CA3\u00F6\u00FB\x06\u009FM\u00E8\u00E8t\u00C3Kz\x11\u00C0D\u00A0\u00B9\u009Dm\u00A6\u00A9$\u00CB\\!+\u0096\u00FBV\x12\\cM\f\x10\rZ9\u0087\u00D8\u0099\u00B9\u00BB\u00C2\u0093\u00F2n#3\x05\u0080\u00AB86X\u00EB4\u009Dob\u00FE\u0098\u009D_\u00B9\u00E4g\u00F6\u00BD\u00E8tT\x06\u00C0i\u00AA\x10\x00\u00E0\u00C1\"\u00FDj\u00D5\u00EF\u00DA\u00F5\u0085\u00C3G\u00DErd\u00FD%Gd\x0E\x00\u00C1\u00AD\u009FZQ\u00BE\u00C8\u00ACy\x0F\u00A1\fSC\u00F4\x1C9\u00E1\u00A0E ]1\u0084\u00A0\u00A2\b\t\u00D2}t7\x1E\u00C0D\u0090\u00A3n\u0081\u00D7\u00E4\u00E7\u00B1\u00D3\u0088#\u00D3\u00F99S\u0092\u00AB\u0093{r^*\u00BB:\u00D1\u00FD~\u00F9=\u00BB\x15-YX\x13}(\u00AD\u00E0i\u008Cs\u00E5\u00BD\x18*\u00EF\u00E7Z\u00DA\u00BD\u00F7\u00A2\x18\u00E0\x1EB\\\u0087c\x17\u00BC\u0084\u00C8\x0E\u0080:\u00C3\x1A\u00AA\u00F0\u00B6'\u00E9XI\u00AF\u00AB\u00C3\u0094V,\u00D9\u00A3EX\u0091\u00F7\x1Cy\x7F R\u0085\u00A3k\x03k\u00A3.q\x1Ef\x0EB\u00A1\u00F8>ld\u00CA\u00FD\u00ADn\u0081\u00CA\u009F\u0082n7\u00C0\u00BA\x02\u008A\u00B2({*d\u00D1\x14i\u00DCCxY\x1A\u00A1\x0F\u00FB)\u0088\u00DA\u00C9D\u00E9=\u00D9\u009DvU\u00B4\u00D9\x0Ef\u008F\u00DFy\u00F5%\u00BF\u00B4\u00EF{\u00E4!\u00A7W\u0098P\u00C7i\u00AB\x10\x00\u00E0^\"z\u0093\u00EA\u00BF\u0099}a<\u00F8\u00DB\x07\x17\u00FF\u00F1\x16\u0099E\u00E3O\x00\x13W\u00DE\u00F4mZh s\u00EA=P}M\u00ABW'\u00AD\u00BA\x00(\u00AB\x19\x01\u0086\x06\u00D5\u009B\u00E0\x04\u00A5`\u00AE\u00B6U\u00CB\u00F0\u00A5\u00DC\x043\x1B]b\u00D2\u00F3\u00DCU\x19To$Zz\u00D5T\u00A5\x0B\u00F3\x04XuA\u00D9!\u00FB\u00D0\u00D3}v\x1C\u0089u\x10\u009B\u00DF\b\u00C9G\u00A3zO\u0088\u00D5\u00F5\x19\u00E2Z\x10a\x06\u00E3o\u00ED\x02\x11\u008D\u009A\x06>n\"\u00FBl\u00B5\x1E\u0082\u00C1\u00EF\u00B9m}vq\u00DF\u00E6UPY\x11w\u00A0+\u00CFc\u00D9\u00BB%(g\u0099\x10\b\u00A2U\u00BA\u0096sq\u00DB\u00C6k.\u00F3\u0084\u00CF\u0087\u00E7'\u008E\u00A0\u00E5\u00BC\n\x03!\u00C7\u009E\u00A9\u00C4j \u00B8O\u009C\u00CB\u0081\u00CC\x16\u00D7\u009F|\x0B^\u0094\u00ACw\fO<\u00F4\u00A3\x0F\u00FA\u008D\u00F3^!\x17\u009E>\x00\u00E2\u00F1\u00C6i\u00AD\x10\x00\u00E0\u0080\u00CD\u00B8W<\u00EF\u00BA\u009Box\u00DBM\x1B?\u00F7%]o\x14\u00F0\u009CX\u00EAVS\u00FF\u00F5_]\x00\x00\x17\u00F9IDAT\u00F2\u00ED\u00BAPL\u00B6\u00C3\u00CA\u00FF\u00EB$r!\u00AD-\u00D4\u00E8Y(_.\u00B2\u00ECZ'B\u0094\x05F5\u00AC\b\u00F7\u009F\x16\u008F\x13k%\u00F4\u0099\u00AC\x05A\x0B\u00AF\x1A\u0093p\x12\u00C6\x14e@\u0080t\u00A9\u00B6X,\u00AD\u00F9\x1ASe\u00BE\u00AD5\x01\x15\u00B4\u0091\u00F8@GG\x0BO\u00C0\u00D6\u0082t\u008B\u00D7\u00BD\u00A0\u00CC\x19\x7Fl_\u00AE\u00DD\u00B2\t|<K8\u00F1\u0086R\u00EE\u00D8\u0081\t\u00B1\u0086pwL=\x05*\u00E5P\u00E4R\x14\u009D\x1F\u008A\u00AF\u0083\\\u0083\u00AEi\u00BD\u00B5\u00D3\x13\u00B0\u00FBi\u00E5\x12\u0082e\u00E8!\u00D5*]\x19\u00AE\b\t\u00C4\u00F6\u00E3.\u008Ab\u00BD\x17\u00D9\x1Eo\u00A6\u00C8\x0ERT\u00A2H\u00EF\u0084J\u0084\u00AB\u008A\fPp\u00E1AQ@\u00F6l\u00F6\u008D\u00AF\u00DD\u00FC\u009E\u0087\u00BF\u00ED\u00FC\u00D7\u00E0\u00C2\u00BFs\u00BA\u00DF\u00E1\u00E3\u00F4VW+\u00E3\u00E5Wo^\u00F6\u00EB7\u00EB\u00EB??\u00AEo\u0090\u0087\u00C0:\u00FE\x10p\u00C6\u0084@\u0096'\u00BB\u00B0\x05\u009E\x10\x02\u00CA\u009C\u00B8\x1FC*\u00E2_\u008E\x0B\u00A40\u00AAZ&\u00A1\u00A3\u009C\x13)\u00A8\u00DCF\u008B\u00DB]\u00B6\u00D58\u00AE\u00FA$N\u00A5\u00C1\u00B5\b\x00\u00BF\u00DE\u00BA\u00BCx\u00D9_\u00D8~<\u00BC\n\u008Bk\u00D7\u00BAb\u00AD\x1B\u008B1\u00DA\u00B3w`>&+p\bwW0_$\u00F9\u0086\u00CC\u00CA\u00B6D(\x06\x02\u0092\u00CD;\u008C\n\x043\u00B6\u00F9\x12o\r\u008FB\u00F2Q\u00EF\u00B9\b\u00F6D\u00D0d\x1F\x164?[\u00B2#Rs\x14\u00B2\fG$HIl\x04c\u00C0\u00A0\u00C4\x020\u00B6\u00F2R\u0092\u0094jk4R\u0087\u00E9\u00F2\u00B3\f;\u00FB$:\u0089\u00ABg\u00F8G\u00A5\u00D4\x1C\u0097\u0099\u00B0\x1By\u00AC\u00CEtd\u0092\u00A6\u0082<\u00C5s\u00B9\u00F2k\u00FB\u008El\u009D}\u00E9\u0091\x7F\u00FA\u00F07_x\u0087\u00D2\u0091Od\u00DC\u00A9\x14\x02\x00\u00BC\u00FC\u00DACOz\u00F3\r\u00ED\u00B7?\u00B5\u00D88\u00A0*e\u00E9+Mp/\u00C8A\u009A\u009E\u0082\u00BFD\x19i\u00C9\u00B5\u00E0\x05\u008E\x0BHZy\u0089NCi\u00D9\x05fU\u00A8\\\b\x0B\u00A5\u00AB?U\"\u0093~\x02.\u00E8:\u00F2$\u0098(\rZ\u00E1\u00C0;|RO\u00C3\x04E\u00AC\x10\f\u00B7\u00E6\u00B1\u00F8l\u00AE\u00DD\u00B0\u00E1\na\u00D6\u0081\u008D\u00A2\f\u00B8\x04\x1C\\1\u00CC\u0096\u00C5\u00AA2L\u00F0&+V<\u00C5\u00CF\u00F8l\x053\u00B8K,y>*\u0084\x01N\u00A1\u00F6YE\u0081d5#k!\x06\u0099\njU\b\u00F3\u009E\x7F\u00F3\u00F3`'\u0082M]\u00CD\x06\u00AF\u0095\u00DE\x0EA\u009D\u00EE^c\x11J$\u00EF\u008F\u00D4kT\u0085@\u00D4\u00B3\u00F3<\u00A9$\u00EB\u00B2r\u00F0k\u00E5\u00B3\f\x05\u0081\fw\u00E2Z\x05\x18/<t\u00D3\u00DE\u00A7\u00F6o}\u00F4\u00AF\u009Ds;tB<u\u00E3N\u00A7\x10\x00\u00E0g\x0Fn\u00DD\u00FFM\u009F[\u00BE\u00F5\u00C3G6.Y(\u00EB\x1F\u00B2\u00A6\u009Fn\\6(Y\u0089\x05\u00B5\x14'\x15R\u008E\u00B9\u00B9\u00DE\u00D8c\u00C5\u00BB\b`\u00B1\u00A7'\u00C0\u00C5U4\u0094L9\x17\u00BD\u0082\u0091\x15\u0085\x00S\u00A3\x00\u00F7\u0097\u00B2~\u00C4T\u00A1d\u00E7\u00E9\u009C\u00A0\u00DA\x11\u008D\f\u00ED\u00BC\\\u00ECEC\u00F14\x00\u00BB\u009C\u00BD8/\u00B5\x0E\f\t\u00B8V\u00E1\u00AC\x1B\u00A5\u0099\u00DC~\x02am\u00F4\x02\u00A8\u00D2pE\u00DCCh\x10/^\u00CA8\u0099\x1E\x02=\u0083\u0099c\r& \x12kLL\u00FE\u008F$\x185)\u0082\u00DF\u00FF&\u0085\u00A0Q<%\u00B0f$\x02\x07\x1F{\u00CF\u0085Z:=\x05\x0B\x1F\u00AB\x05\u00E7\u00FD\u00C7\u00FD\u00C2\x00J6X\u0081j\u00D4\u0081\u00E4\u00FA\f\x14|\u008D9c\u00CA\u00AF\x15E5U\b\x02\u00C5p\u00F1\u0091+/x\u0086<\u00E3\u00C1\u00AF9\u00FB\x0E\u00ABZ\u00BC\u00B5\u00E3N\u00A9\x10\x00\u00E0\x7F\x1E\u00D5\u00FD\u00BF\u00FC\u0099\u00AD7\u00BD\u00F3\u00E0\u00F0\u008D\x0B\x1D\u008C_\x00)18B\u00D0B@\u0081@\u00C4\u00A8\x104\u008A\u00A3\x14\u0088\x14\u00D2\u00B4\u00DB\u00D2$\u00CC\u0088\x10\u00C0YkhSP\u0090\u00C5R\u00BE\u009Dj\x0F\u0082O\u00C6\u00AA\u00D9\u00DC\u0094\u00EEzz\x19\u0092\u00D7Lo\u00A5x\x10R\u0094\x18F6YEx:\x02`\u00BD[\u00CD\u00C3Z\x07\u00E6]\u00AC\u00B7\"h\x15]\u00E8\u008B\x17@!\u0081\u00FF\x7F\u00E8~,\u00B6v\x1B\u00CD\u008D\u00B6\u00B6h^; \u00C5\u00A2\x02\u0098Ac}o\x13Pq\u00E1e\x1D\u0083F\u00ABv\u00B6M\u009B\x15\u00E1\u009Fx\bj!\u00C9P>\x1FT\u00DDKH\u00EBl!G\u009B\u00B4Q\u00A7\x07\u0093L\u00C3\u00A9\u00E0\u00D6\u00B6h\u00ACFl\u00E59\u00B3\u00FE\"i\u00CC\u00A6\x10\"\u00E4\x00\\\x1D kF,\x7F\u0082&K\u00AC=\u00EC\u00C8;\x1E\u00F8\u009D\u00EB\u00CF\u00BE\u00E8\u00DF\u00EE\u00BEC\u00FA\x19\u009C\u00EC\u00B8\u00D3*\x04\x00\u00F8\u00A0\u00EA\u00F0\u0093\u009F\u00D9\u00FA\u00E9\u00B7]/\u00DF{x\u00B1\x06@&\u00D6VV\u0084+R\u0088.\u00D0\u0093\u00BC\u00B1\x0B\u00AA\x05\u00F2\u00E4\x17\x14\u00B0/\x14\nR\x00\u00A9H\nEZ\u0099\u00FB\u00EA\x05\x1FP\u00B2#\u00BDwB\u00D7\u0089\u0092\u00A9\u00FC\u00F9\u00D5\u00AEM\x01,v\x05z\x07\u00D7\u0097Hfa\u00D6\u00E5\u00F3x\u00B3nK\u00BB\u00CD}\u0082ot\n\x0F\u00AD\u00AA\u00F5\x10\x14\x05\u00DAR}\u00CD\x066\u00F5\u0090P\b\u00F5Y\x0EK\u00B3\u00FE\x112\u00C0\u00AB\x14Qp\x04\n\u009C\u00A4`\u00B1z\u00B1\u00A9\u00C6\u00DA\x0E\u00EC\u00930\x1F33\u00C1T\u00E4\u00DC\x15j\u0093\x04\u00FF\u00A2h\u008Ba_Q$3L\u00DDx*\x11\n+\u00F7\u009F\x14\x1F\u00D5\u00FFs\u00BB\u00A2d&\u00E9\u00CC\u00AE\u00A1\x042{P\u00CF\u00E5+J\u00ED\u00DA\u00C6\u00FE\u00AF9\u00FC\u00AA'\u00FE\u00E4y/\u0093\u00C7\u00DF\u00FE\u009D\u008EN\u00D5\u00B8S+\x04\u008E\u0097|\u00F6\u00D0s\u00DE\u00FA\u0085\u00D9/\x7Ff{\u00FD,\u00A2\u00DA\x13\u00E6\x1B\u0095\x000\t\x19\u00A2G>\x15\u0087\x7F\x1Fi\u00B0\u00A8<DZ\u00F7\x1A\x1E\u00F0<\u00DC/\u00BC\u0087\u00E2\u00F2W\u0090\x10\x05/\u00F0N\u00C7\u00F6]6x\t\u00E5\u00C5\u00E3D\u00C1\x13\u00CF'\u00A9\u0094\u00E850\u00B5\x19\u00F8\u0083\t\u00F7Z7\u00A5\u0090\u00EBCf'\u00A0\u00A1x\x033\u00F6Sw\u00AF\u0083\u00DFE\u00C8\u00E3\n\u00A3)0\x0F\x0F\u00C1\u009B\u008DR\u00F8\x1D\x17\u0098\u008F\u00A6\x10R\x19\u00F0\u00EF\u00EE\u00DEE\u00BA\u00F2\u00F3\u00DE\u00AD\u00C3\x12\x12p\u009Cw@\x1D\u0094\u009C\x05\u00DF\u00C3Vz\u00E6~\u0083\x16.\u0083\u00DF\x17y\"\x14~\u00AE\u00E3\x00\u00AC(\x04\u00CD{\u0089\u009E\tE\u0099\u00B0d\u00D9\u00C2FSb\x00\u0095\u0096\x1F\u00B3\u00A7\u00A7\u00D2\u00DA\x12\u00B3\u00FD[G.x\u00F2\u00F6w=\u00F6\x7F\u009C\x7F\u00BBuG\u00BE\u00AD\u00C6]B!\x00\u00C0\u00CF}q\u00EBa\u00AF\u00FF\u00D4x\u00F9\u0087\u008F\u00CC\x1F\u00A4:\u009F\u00BA\u00EF\u00A3N\u00AC\u00BDW\n\u0098\u00D0BV\u00B2\x11\x1A\u00827\x11N*\x14\x12\u008E\u00FC'\u0094Fx%\bkO\f \x04\x0B\x00\x17x\u00E5JI\u0093\u00C2\"*\u0084\x10\u00F8\f%\u00C2\x0BXN;N\x13\u00DC4\u00AF\u00A7\u009Brq/\u00A7u\u00C1\u00C6\u0098\u008A\u0081V\u0096\x15\u008D\u00CD=\u0089!\u00C2(S&\u00C3h)=fHH\u00B42\u00CC@\"S\x10\u0099\x04\u00CD\u0086)\u00D9\u00A2}U! \u009A\u009Ed\u00F6@#\u0086\u00E763\u00CF16\u00D5\u00E8\u0093`\u00F8\u0082\u00E4\u00F1\u00AA\x10\u00C3\u00DB\u00A5\u00A3X|5\u00EF\u00AC\u0082\u0082\x15\u00A7\b\u0085\u0080T\n$/\u00F1\u00FA\u00BB+\x0E\u00B6\u0095\x17\u0098\u00A2\u00E3\u00FB!\u00C8\u00B9v\u00FF\u0083W\u00DC\u00F7i\u00F3\u00CB\x1E\u00FA\u00EA=\x7FyJ&\u00F2\x1D<\u00EE2\n\x01\x00\u00FE\u00F7B\u00CF\u00FE\u00D9\u008F\x1D\u00FA\u0085w\u00DD\u00B4\u00F1\x1D\u00B7\u00F4\x19\x02\u00C4\x1B\x11\u0084&3\u00CA\u00DE!\u00A9\x03\u0082\u0096\u008C\u00C6b\u00E1\u0083W\u0080\"p\u00C4\x05P0\n\n\u00AB\x0Bo\u0086\tU\x19\u00F8\x0E\u00C5s@\u00F7\u00B5!\u00A5M\x04\u009CJ,\u00D6i\u00A7\x07SHJ5L\u0088\u00A2\u009E^\u00F7\u00F1\u00FDF`\u00CDC\u0087y7\u00EB\u00CD\u00CC@\b\x14ADNtv\u00FC\u00F1g&.\x18\u00FC{\u0086bM\u0091B^\u00B3\r\x12\x16\x1E\u00C7\u0082\u0089U8\u0091x\x02\u00E3yz\x1B\u0083+-\u00EE\u00DF\u00CAv\u0091*\u00D5\u00F4*\u0080\u00A2\x10x\x1E\x7F\x16\u0091\u00EA<\u00C6C \x00Y\x15\t\x1C\\Ne\u00C4\u00B0\u0088^\x01T\u00D1\u00D6vp\u00EEW\x1F}\u00E3W\u00BFt\u00FF\u008B\x0F|\u00FB\u00ED\u00B3\u0088\u00CA\u00ED1\u00EER\n\u0081\u00E3\u00A5\u009F<\u00FA\u009Do\u00F9<^\u00F3\u00FF\u00B67\u00CE\u00A6\x15\u00AF\u00BD\x01\u00D2\u00B5\u00B6\u00EDW\u00DBj\u00C7\u00DFE\u00A0\u0093c\u00E0\x0F\u008D\u009E\x02)\u00C7E\u00A1P\u0091T\u0097~\u008A\x17\u00F8u\u008C@\u00E0\x1E\u00FD\u00D8\u00F3S\thm\x1D?vpm\u00888\u00D7\u0088HY\u0092\x12\r\u00CF>\u00C8h\u00AE73\x0E\u00F3\u009Em\u00C0\x18O\u00CF\u0096\u009Aq\u00F4\u00D2-\u00F1X\x04\u008A<\x04ZL5\u00F7\u0099\u00D8\u00C1\f+\u00DE\x00hm\u00D5+\u00FC\u009C\x0BQ\x05\u00DB\u00EE|B\u0083\u00E6o\n\u00FD\x04\x13@\u00F9\u00BF&\x1F\u00C1\u00C2\x03{\u00DC5\x14 \u00EE\u00C0\u0090\u00AEr\x1F\b$\u009A\"\u00C8\f\u00CB\u00F1\u00D2\u0088@*\u00B2A\x15\u00DA:\u00DA\u00B9\u009B\u0087\u00EF\u00F1\u00A4\u00ED\u00EF~\u00F2\u00FF<\u00EF\u00BF\u009D\u00E4T=\u00ED\u00C6]R!\x00\u00C0/_\u00B7|\u00C0\u00AF\u00FF\u00F5\u00D6\x1B\u00DE}\u00E3\u00DA\x13F\u009D\u0085\u00B55,\u00C1-\u00A9\u00F3\x0F\u0092\u00FC\u00A3\u0089'\u00ACX\u00F8\u008A\x1B\u00D4\u00B4bd\b\u0082;\u0090\u00DB\u008A+\u0092\x001\u0091\u008A\u0089\x1EB\u00ACVCE\u0080<N*\x1D*\n\u008D\u00EB\u00E65t\x0F-\u00B2\u0083S+\u00DC\x07\u00C3*\u00B8(\u00EC\u009AZ\x1F\u00C6A\x0B\u00B8\u00E6\x18BcX0\u00A6\u00B2`\x11\x0FF\u00CD\u00AEKZ\u00C3\x02c\u00E6\u00B1=Z\b\x0E3\x10\u00DD\u00B1\x05\u0098[\u009F\x19\b\u00EE\u009F\u0096:\x17l\u00F1\x15\u00A9\\\u00A9\u00D6~\u008B\u00E1! \x15\x02\u00F1\x10\u0085\tw\u00AC\u00D0\u00A4\u00C0|\u0094\u0088\u00F7\x1B\u00D2\x13\x10\u00CD>\x05\u00D5[R\u0092\u00AE\u0090\u00A1\x03x\u00CD\x00\x1A\u00B6\u00B0\u00EBA\u00B7\u00BC\u00F7\x01\u00DF6<\u00EF\x11?z\u00DEm\u00BA\n\u00F3\x1D5\u00EE\u00B2\n\x01\x00nP\u009D}\u00DF\u00C7\x0F\u00FF\u00C0;\u00AEn\u00FF\u00E1\u00BA\u00CD\u00B59tf2\u00E7\x02\u00CE\u00BC\u00BE\x1CO\u00A0};\u00A0\u0084\t\u009E^\u00A40\x06\u00BF\u00A0\u00B8\u00AC\u00BD\u0097\u00E3\u00AD\n4\u00A6\x1E@\u00BA\u00FAT2=\u00F0\u0087)x\u00A8\x01v\u00C2\u00BD\u008A\x00,\u00A9\f\bL\u00B2\u00D6\u00A3S!e\x0F\u00C59\x14\u00EBKKCJ\u0097X1\u009A\u00B8\u0082t\u00B16jc\n\u00B4:S\u0091\u00D7\u00CD\\\u00BD\u0088uIb\u00CFDZ\u00FF\u009AV\u00AC`\u00E3\u00CC\u00D3\u0096)\\\u00CE\u008B\u0080L\x04\x1E\u00F0%\u00EBz\x11H\u00CD\u0098\x7F\u00E8\u0092<\x06\u00F7\u00A8\"\u00E4P\u00A3`\u00DBwb\u0085P\u00BA\x12\u00A6(\u008F\u00B9\x12&(\x00\u0099z\"\u00D6\u00EE\f\x10\u008C\u0098\u00ED\u00DBY\u00EC\x7F\u00C2\u00D1W>\u00FDG\x0E\u00FC\u00B8<N\u0096'=9O\u00D3q\u0097V\b\x1C?\u00F5\u00D9\u00A3\u008Fz\u00CBU\u00FD\u00BF~\u00E0\u00C6\u00B5G\u00F6>\u00CF\u00EE\u00C8$\u00F90\u00D5\x17\u00C2\u00AA\u00A8\u008B\u00AE\u00AC2\x11\u0089\x01\u00D4\u00DC\u00FF\u0094\x15\u00A9\u00A1\x10Hf\u008A,Fa\u00D7\u0099\x05\u00F7\u00CF\u0095\x1C\u00FA\u00E45\u00D0\u00C3\u00A0\u00F5'M\u00B9\u0086\x14\u00E4!\x18\u00960\u00CD@\u00ACfI\u0086n\na\u00DE\u00E9\x19 \u0080\u00B6\x16\u00D4e\t\u00E5\x10k\x1BF\x16B-\u0095)m\u0082\x11Tl\u0080L\u00C4\u00CAQ\u00E0\u00B6\u00C1+\u00A8\u00FB\x00\x13\u00AB\u00CE\u00EF\u00AAg@\x16\"\x15B+\u00E7k\u00FE\x1E\u00B2\u0083sV\x1E\u0092;0\u00A10k\u009EG\u00FC\u00B8\x11>\u0094\u0085g\x07\u00D8\x1F3Yb\u00EF\x03\u00B6>|\u00CF\u00A7.\u009F\u00FF\u00A4\u009F?\u00F7C\u00A7|r\u009Ef\u00E3n\u00A1\x10\x00\u00E0}\u00AA\u00F3\u00D7\u00FE\u00F9\u00C1\u00EF\u00FF\u0093k\u00D7\x7F\u00F0\u00AA\u00A3\u00EB\u00EBY\u009C$S\u008F \\w\x14\u00C1C\u00C4\u00E9\u00AB}\x15\x01W\x14\x10T\u00A6a\x05\"Y\u00E84\u00E14\x14A\u0095n=\x04\u00C2\u00D2\x17\u00A1\u00D61\u00AB\x14\u00B9\u00EC\\\x1E\u0087\u00A9KR\u0098\u00CB=\u00B8\u00D0O\u00C0\u00CF\u00AE\u0098\u008F&\u00E8\u00A46\x1BkQ\u00A39h[z7\u0083n\u00EB@\x06\x0E\u00E2\u00CAf\u00EE\u00AE\u00BF\u00C8T!DCUMjr\u0082\u008A\u0096Q \u00A5\u0098.9=\u0089\u00B9[gvB\u00AA\x1EF\u00C4\u00F5<NG\u00F4P\x18\x14Q\u00EE=\u00F8s\u009C\x00\u0091\u00FE\u00FEV\u00BD\u0081\u00BA\u009E$\u0090\u008A\u00878\x03`D\u00AE\u00E1\u00C0\u00C1\u00ED{>Z\x7F\u00EC\u00E9?p\u00EEO\x0E\x7FO\x16'5\x01\u00EF$\u00E3n\u00A3\x108~\u00ED\u00BA#\x0Fz\u00FD\u00C7\x17\u00BF\u00F0\u00DE\u00EBw]\u00BA=\u00CE0\u00F6\x16a\u00C4\u00B4W\x02\x02\u00B4\u00E3:\u0082(\u0082\x01wc\u00ED\u00CF\u00A9e\x060\u00C9XT\x1E\u00C3jQT4Q\u00ED\u00A4!\u0097\x10\u00C3\u00BD\t\u00BA\u00B9q-%L\u00806\u00AF\u00E7\u00C8\u00FDB\u00D9uW\"]\u0081nB>\u0090{\u00D0\u00D3\n\u00CF:0\u008C\u009EI\u00A0@\u008DV\u00EB\x10)Wz\b\u00A3\x1CCH2k\u00EF\u0099\x05\u00CB\u00DB\u0080]\u0099i\u00A5\u00C5\u00D3\u0088\x03$)\u00CB\u0098\u00A6\u00FA\x02\u00F4\u00C3\u00B1\u00C0b\u00F3d\u00B1-\x02+\u00A1\x1C\u00E8m\u00CDz\x12\u0099\u00C0\u00EBQ@\u00A4\u00B6s/\u00FC\x03\x14O\x01E\u00B9\u00A9B\u00E6Gq\u00EEC\u0097\u00EF\u00BA\u00E4i\u00FD\u00C5\u008F\u00F9\u00F1s\u00AF8\x15\u00F3\u00EE\u00CE2\u00EEv\n\x01\x00\u00BE\u00A8*?\u00F1\u00B1#\u00CF\u00FB\u0083\u00BFn?\u00FD\u00F1\u009B\u00D7.\u00EC:\u0084\u0090O*\x1C\u00DD\n\u009B[_\u00C0>n\u00AB@\x10\u00A1h\u00991\r\x07\"\u009E\u00D7\x12>\u0084\u0087a\u008Af\u00B5\u00A4\u009A\u00CDM'\u00A5\u00B9\u00A0\u00C7\u00D1#$\u0091\u00DE&\u00DEDz\x02\u00C80#>\u0097\b#\b\u00C4\u00CD\u00C7$\u00E3\u00CC\u00D8\x15i\x04\x00\u009D(\x04*\x1Cx\x18\x11\u0085L\u00A8\x1EBw\u00901\u0089C\u0095\u00AF\x00\u00FF\u009Bt\u00E5\u008A\x03$\u00BBP\u00A7t\u00E5n\u00DD\u00A1b\u0089w0\u00F4(\u00ACAw\u00EF\u00E8\u00F5\u00A4\u0090'\u00EE@B\u0091\u00F8\u00B5\x05\u00B7\x01\u00F6\x0E\x1B\x18\u00E6t\u00EC\u00BE\u00F7\u00E6u_\u00F1\u00B5\u00CB\u0097=\u00FD\u00D5\u00E7\u00BCA\u00CE\u00E7Y\u00EF>\u00E3n\u00A9\x108\u00FEd[\u00F7\u00FD\u00E2\x07\x0E\u00FF\u00D0\u00BB>/\u00FF\u00FA\u00BA\u00C3\u00BB\u00E7Q\u00D3O\u00CB\u00CAJ\u00CAb\u00E9\x05\u00C8\u00AC\x00$\x1B\u0088v+t\u008A\u00B4\u00A2\x0Ba\u00E57\u00B0Y)\u00AD\u00ADhK\u00A0\u0090\u00FB\u008C+a\t*\u0086\u00D1\u00CB\u00B1\u00E4\u00B8\u00C0c\u00D0\u00B5\u00EB\u00825\x04\x05\u00A9\u00A4\\A\u0091\u0097\u00C0\u00D4]\u00EB\u00C0\u00B0T\u00A0[\u00FD\u00C5|l\x19~\u00F85\u00B6\u009E\u00A1A-}\x1E\\\x00\t0r\u00C5\u00A6Y\u00B0\n\u00CDk\u0098\u00BB\u00CB\x1Fk5(\u00A6\u0098\u0080\u00D7\u00944X\u009AT\\\u00F1\u00D6\u00E5\u00E4#EI\x0F\x04\u00CC\u009A\u00E8\u00E4\u00FC\u00C2\u00CC\x0E\u008F/\u0080h\x0B\u00CA\u00B1\u00A83%\u00A5cc\u00FF\u00F6\u00E2\x1E\x0F\u00DF~\u00ED\u00D7\u00BFp\u00DF\x0F\u00DF\u00FB\u00B9\u00B3\u0083\u00A7|\u00B2\u00DDI\u00C6\u00DDZ!p\u00BC\u00F6\u00FA\u00EDK~\u00EFC;?\u00F3\u009Ekf\u00CF8\u00B2\u00B3\u00EE\x0B\u0082f\u009DCm'\u00C6N\u00C9\u00C7\u00C4\u00F3>q3\u00AD\u0088\u00E47L\u00C2\x07\u00AE\x07\u00E9\u00DD\u0097+n\u00A1\u00F0\u0096f\u00E9Q\x00\b,\u00C1x\x06\r\u00AC\u00C1\u0098tm\u00A6w\u00C0\u009E\u0092\u00A5\u00ACz\u00EAAx\x1B\u00B1\u00D1\u00AC*\x19\u008B\\\u00E8\u00A5-5\u0084kXz\u00B1\u00D7\u0098@j\u00AC\f%iy#\u00DD\b\u00B7\u00F2\u00C5\u00A5O\x0F\u00C1\u00DA\u00A4\u0093Dd\u00C0\u00A1\x06\u00907\x14\u0085@\u00AB\x1E\u0085N\u00C8\u00F0\u0081J \u0084\\\u00A7\u00DBP\u00B9\u00F9z\u00CF\u00B1\u00DA\u0093(\n\u00A5ZC\u00F1\f\u00BB\x0E\u00E3\u00A2\u0087\u00E2\u00AD_\u00FD\u008C\u00E1\u00A5\u008F}\u00C5YW\u009E\u00D2\u0089u'\x1Cg\x14B\x19?\u00F1\u00C9\u00A3\u00DF\u00F0\u00FBW\u00F4\u009F\u00FA\u00E0\u00D5\u00F3\u00C7m\u00F6\x19b\u00E5\u00A4\x11`S\u0093\x00\u00FD\\\u0080k\u00A7\u00E4\f\x0F\u00B2%x\u0084\x0F\u0091\u0095(\u00E1\x00\u00BB\x13\u0091\u00BE\\\u00B3\x18\u00A1h\u00E0a\x02\u00CF;\u00F5X\u00D8\u00BA\u00D8\u00CE\u00A9%\u009C\u00E01\u00BB\x0B\u00B4/Q\u00C7\u00E6.%Dh=\u00B3\x00\u00ACoh]\u00D1\u0096\x12\u00CA\u008C\u0095\u008F\u008D\n\u00C1\u00C3\x03\"\u00F2\x0348\x04\x04\u00FD,#\u0091\x02[C\u0086Ua\u00AE\u00DF\u00D1k \x1FaU!\u0084W\x01\u00842\u00A1sOO`\u0082K\u00F0oq\u008C\x00\x00f[\u00B8\u00F0\u0081[\u00EF\u00BF\u00FFS\u00F1\u00F2o|\u00D5\u00B9\u00FF\u00EB6\u009ARw\u00BAqF!\u00AC\u008Ck\x16*\u00BF\u00F0\u0097\u009B\u00FF\u00E8\u00ED\u009F\u00DA\u00FE\u00E1\u008F\\\u00BB\u00EB!\u00DB}\u00EE\x02\u00ECe\u00D2\u0085\u00B5\x18\u00AE?\x00\u00AE\t\u00D9\u00BADCNV\u00DB\x05\u0093\x10\u00E9Ap)\u00F6\u009A\x05\u00A8\u00A0\u00A3N\u0094H*\u0099)\u0093q\x1A\x02\u00C0\u00C3\x04\u0092\u0089\u00A8p\u00F2s\x01\nP\u00A8T\b\u009AJ\u0080$&\x19\u0093\u00E5\u0088\x0EH,\u00EBV\u00D2\u0088@\x10\u0091\x06e\u0097$\t\u0081\x1C:\u00A2\u00DF\x01\u00BA\u0085\x10s\u0095\u00E8\u00C6\x1E\x0B\u00BA\x02\u0098\u00ABN\u00AD=Jz\u00B1\u00F3\u00F8\x12a\x14\x0B\u00AB*\u00C7@\u0080Ia\x15y\x10\u00E6U\x18(9\u00C76\x0E<x\u00E7\u00E3\u00F7y\u00E2\u00F8C\u00FF\u00F0\u00A7\u00F7\u00FF\x0F\u00D9\x7F\u00F7\u00C3\t\u00FE\u00B6qF!\u00FC\r\u00E3\u00F3\u00AA\u00C3\u00AB>p\u00CB\u00B3\u00DF\u00F3\u00A9\u00F6\x1F>t\u00ED\u00ECA;}\x1D\u00D10\u0095`!\u00A6 \u00A1\u0096\u00DF\u00ABl\u00C6\u00E43\u00ACd$\u00A8\x1C\u008E\u00F1\ntE\u00F10l\u00D1\u00F8;\u00EB+\u00E8\x150\u0084\u00D0\u00C0\x13t\u00B5\u00D4ztO\u00C3\x15\x12\u00AF\u00DFV\u0086\u00F6\u0085Y\u00F9\u00DD\u0098+\x18K\u00C1!fj\u00EC\u00C4\x106a\u0083\x14Zo\u00EF\u009D\x10\x02\u00C9\u00E6\"\u00CE\u0081\u00F0g\u00CC0\x03\u0092\u008D\\(\u00D8\u0095\u00C7P3\x0E\u0099\u0086,m\u00DA\x14\u0096fDvs\u008A\u00F0\u0081\ni\u00D8\u00C4E\u0097l^\u00F1\u0095\u008F\u00D7W~\u00DB\x0F\x1Ex\u0093|\u00D5\u009D\u00B7D\u00F9\u00B6\x1Cg\x14\u00C2\u00DF1nP\x1D~\u00E6\u00CF\x0F}\u00FB\u00FF\u00FE\u0084\u00FE\u00FB\u00BF\u00B8z\u00E3\u0091[\u00E3\x1C\u00D1\u0099\u00B9g#US\x04:\u00C1\x10\b\u0088u-K\u00A6\x05\rZ\x0B~0\r\x13&MO\"\u00A5Y\u00D6J(\u00E7\u00AAaB\x0B\x0ECn\x1B\u00DC\u0088\u00DE}?\u00C9\u00DA\x0E*\u0095\u00D1\u00B6\x1F\u00BA@F5\u00D7\u00DD\u008B\u009B\x06 J\u00A0\u00A1@s%1\u00D7\u00E4#\b\u008C\x16m\u0096=-t\u00F6IP\u00F7\b$\u00B0\u008A\u00E0\x00\u00F8\u00CF\u00BC\n\u00BB \x17\u008E\u0091\u00A2\x10\u0090^\u0085\u00BAWR\u00B9\x0E\x02\t\x16\u00A5\u00A5\x1C;\u00E6\u00F3M\u009C\u00F3\u00A0\u00C5\u0087\u00EF\u00F7\u0084\u009D\u009F\u00F8'?v\u00C1o\u00C9yg\x14\u00C1\u00DF6\u00CE(\u0084/s\u00DC\u00A4*\u00BF\u00F8\u0091[\u00BE\u00E9\u008F>\u008E\u00EF\u00FB\u00F0\u00E7\u00E7\u00DFpp{\u00CD\u00AC\u00EF8K\x10\u00B0\x02\u0089,o\x06\u009CX$\u00D30A1\u00E5\x19\u00D0\u00C3\u0088B\u00A5\u00C2\u0094t\u00A1]]T%HC\x05\x1F\b2\u0093V\u00A6c\u00CF~\x10\u00A3D\u00E5'\u0097\u0088\u00AB\u00EBA\u00A2K\u00B4P\u0093\u00DEm\u00F9s\u00E7h\u0088Z(1\u00C0:1\u0091\u00C8\x13\x1E\u0082\x0B4-4\u0097\u008D\u00E7\u008A\u00CA\u0086\x13h\bpM\x1F2Kaq\u00BEd\u00FA\u0091\u00C7*\n#AE-\u00D8\u0080g\x19\x00\u00B4\x06\u00CCv\x1F\u00C2=/\u00D9\u00FE_\x0F\u00FFz\u00FC\u00E7g\u00BE\u00EC\u0082?\u0090\u008B\u00CE\u0084\x06_\u00CE8\u00A3\x10n\u00C5x\u00DD\u00A7\u00B7\x1Eu\u00F9\u00FB\x0F\u00BF\u00E4\u0093\u00D7\u00AC=\u00FBs7\u00ED\u00DE\x18\u00D1\f\u00D5\x1E%\u0080H\u0096CG\u00DD\u00C3\u0098\x169\u0081\u00C8\u009A\u00A5pE\x10\u00DC\u0085Z\u00E1X>\u00AF\u00A0\u00A2f/\u00C5\x00\x1DGWD\u00DD\u0081E\u00ED\b\u00A6%$\u00AE#\u0088OU\u00C1x3\x16\u00E9ITj\u00DEg1\x14\u00D1h\u0085E\u00F3\x1A\u00A3\u00A3T<V\u00AB\x0ED\x01\u00D3\u00E0\u0084%\u00F6<\u009C\u00D4\x17\u00C0B\x00\u00B3\u00FE2\u00A15\x1B&P\u008B\u00A0\u00A4(\tE\u0093\x0E\u00A0y\u00B8\u00D0\u00B1\u00FF\u0082\u00A3[\u00E7\u00DDw\u00E7MO|\u00D6\u00AEW\u00FF\u00FD\u0097\u00EF\u00BE\u00CBS\u008DO\u00F58\u00A3\x10Nb\u00BC\u00E7\u0090\u009E\u00FF\u00BA?\u00BD\u00E5\u009F\u00FF\u00E5\u00D5x\u00D1\u00C7>7\u00BB\u00DF\u00D6\u00F6\x06 \u00CD\u0090} \u00847]t\u00F5\u00EAD\u0084\x070\u00A9U`\x16\u00C1\u00853j&jj\u00D2\u00C1\u00CB\u00BA\u00D2S\u00E0\x02+!\u0087Q\u0097uJ`\u00A2\u0097r\x1C\u0085@\u009C@\u00C7\u00EE]\u0093\u00DA\x04?\x10\x05\u00DA\u00C8\u00B6\u00E7\x12\x1D\u0096\u00A3\x7F\x01d\n\u00E4)\u00D0\u009A\u0096\u00BE\u008A\x12\u00D6~V\u0094\x070\r5\u00C87\b<\x00\u00EC\u00A7\u00E8\u00CA\u00C23\x17\u00B3\x0EH\x1B1\f\x0B\u00DC\u00E3\u0092\u00FE\u00E9\x0B\u00EEw\u00E4u\u00DF\u00FC\u00C2\u00F3\x7F\u00F5\u00C1\u00CFl7\u00DC>3\u00E0\u00AE7\u00CE(\u0084S0nPm\u00BF\u00FA\u00C1\u00AD\u00A7\u00FC\u00D1G7_p\u00E5\u00B5\u00C3\u00B3\u00AE\u00BEq\u00D7F\u00979d\u00EC\u00D0\u009E\u00E5\u00C8\u00E6\x01$m6\x00H\u00EF\u00F8\u00B3\u00DA\u0082\u00AD\x15\u00C1\r\u008F\u00A2\u0084\t\x13\u009EAaE\u00B2{R\x1B\u00C5\u00D6.\u00EC\x19&P\u00C0'@\u00A8\u00EF\u009B\n\u00C1\x0B\u009DF\u00C9\u00BF\u00BB@tD\x1B\u00CD\u00E1\u00B7\u00B4\u00A2U;\x1A\x01)\x05\u00BAIV5\x0E\u00A2\u0091%\u00A8\u00B5\fMR\u00A1\x10,\u0094\u00A6%4(U\u008D\x00\x04=k%\u009Ab\u00AE#\u00F6\u00DEck\u00EB\u00C2\u008B\u00FB[\x1E\u00F1u\u00F2+\u0097}\u00EF\u00BE?\u0096{\u00B0\u00A6\u00F4\u00CC\u00B8\u00B5\u00E3\u008CB8\u00C5\u00E3\u00A3\u00DB\u00BA\u00FFu\u00EF8\u00F4\u008F?x\u00D5\u00F8\x1D\u009F\u00B9v\u00F6u_\u00B8e]0\x0E%\u00BB\u00E0\u008F\u00FC\x18\x001]y\u00F5X=\x0B\u00A4\u00DC\u009B\x00\u0080\u00B1\u00B4U\u00EB2\u00B5\u00F8\u009E\u0081`wf\u00E9\r\u00DD\u00B9\x13Qe\u00E9\x05KUy\u00C4Z\x15\u00A3\u00A5!\u0089u\u0084\u0097\u00C2f)\u00DD\u00BEg\u00E5!\x15B\u0083Z\u00B3S\u0086\x02\u0092\u00C2L\u009As\u00E5\x05\x10+`\u00B6\x02\u00D2]\t\u00B4l\u00AC\u008A\u00C4 \u00C4=\u0084&\u0082=\u00FB\u008E\u00E8=\u00BEj\u00FC?\u00F7{X{\u00E37}\u00E7\u00DE\u00FF\u00FE\u00C0K\u00E5N\u00D9\u00DD\u00F8t\x1Dg\x14\u00C2m8\u00DEv\u0093^|\u00F9;o\u00BC\u00EC\x13\u009F\u00DB}\u00D9g\u00AE\u00E9\u008F\u00BF\u00E1K\x1B\u00B2h\u0080\u00F6Y\u00A4\x13\u00938d\x19\x00x#\u0092\x1A&\u00E4\u0092\u00F4Z\u00D2\u0089\u0098x\x05LSF\x1B\u00F9\u00B1\u00B4V+\x00c\u00F5\x0Ejed\u00FDa\u00F6\u00A3\u0091\u00E30j\\#y\x0B@\x12\u0092\x06\u00F1\u00DF\x10\u00D4\u00F6\u00EB\u0081/\x1400\u00BD\b\x0F%JS\x12\x02\u008F\u00CC.4\x05\u00B4m\u00E3\u009C\u00FDK\u00BD\u00E7}v\u00DEw\u00AF\u008B\u00FB\u00E5O\u00BB\u00EC\u00C0\u00E5\u008F~\u008E\\u\u00DB\u00BF\u00BD\u00BB\u00E78\u00A3\x10n\u00A7\u00F1\u00A7\u00D7\u00EAW\u00BC\u00FEO\u00BF\u00F8\u00AC+>7|\u00CBu7\u00CC\u009E\u00FC\u00E9\u00EB\u00DB\u00BA\u00F6\r\u00F4>B\u00FAP\\\u00FF$\x16\u00B1\u00D1\u00CA$\u00ADY]\u00FF\x122\u00B0\u0085Z\x1Bk\u00AAq\u00CAy\u0098\u0084!\x05C\u00A0\x17\u0090\u00C5U\u00E4!\u00A4\x12!\u0086 E!4'\x18\u00AD\u00B9R\b\u008C@\x10\x1D\u008BY\u00B7\x10\u009C\x02$.@\u00C6c\u0093\x1E\x15\u0088\u0096\u00A5\u00D8\u00C2E_\u00B1\u00D8>\u00FF\u00C2\u00FE'\x17\u00DF\u00B7\u00FF\u00CE3\u009F\u00B7\u00E7-\u008Fx\u00FA\u00DA\u00E7o\u00CF\u00F7uw\x1Dg\x14\u00C2\x1D0>\u00BE\u00D0\u00B3\x7F\u00F3O7\u009F\u00F2\u00BE\x0Fo~\u00E3\u00F57\u00F7\u00A7^u\u00ED\u00EE\x07\u00DC|x\u008E\u00DEg\u00C1\u0084\u00AC\u009C\u0083\u00EA\x1D`d\x1A\u00D3>\u00EF$*\u00D5\x02&\x0F\x13j\x0B\u00B7\u00C8\x12\x14\u00E5\x10\u00D8\u00C3q?K\u00E5\x12e\u00D1\x1E\u00CAp\u00DA\x18\u00E3P\u00B1\x06\u00F1\u00DA\u0086\x04\n[\u00CFjH\u0092\u0093\u00A2\u00AB\u0092\u00A470\x03 \u00B2\u0085\u00B3\u00F6\u00EE\u00E0\u009E\u00F7\\~\u00F2\u00FC\u00F3\u00C7?|\u00F0c\u00E7\u00EFx\u00CE\u00F3\u00CF\u00F9\u00E3\x03\x0F\u00B9\u00EB4/\u00BD\u00B3\u008C3\n\u00E14\x18\x1F\u00B8Q/~\u00FD\x1F|\u00F1\u00D2+\u00AF\u009A?\u00F9\u00FA\u0083\u00FDI_\u00B8\x01\x0F\u00B8\u00F1Kk\u0082\u00E5\x06\u00FA\u00D8\x01VE\u00BA\u0092hZ\u00D8\u0087U\u00E0'!@\u0082\u008E\u00D19\tn\u00FD\u00C9+\b\u00D0R\u00DD\x13\u00D1LK\u008E\u0088B\u00AB\u00C8Bxf@\u00FD\x070\u00D7\x7F\x069\u0086\x1D\u00C8\u008E\u00CC\x19\x06X\u008ApP\u00C5 K\u009Cs\u00EE\u00B6^x\u00E1\u00E2\u0093\u00FB\x0F,\u00DF}\u00DF\u00FB\x0F\x7F\u00F2-\u00CF=\u00FF]\u008Fy\u00CA\u0099P\u00E0\u008E\x1Eg\x14\u00C2i8>pT\u00CF{\u00F3\u00DB\u008F|\u00CDG>q\u00E4q7\u00DC8\x7F\u00EC\u00E6Qy\u00F4\u00E7\u00AF_\u00BB\u00E8\u00E6\u0083\u0082\u00D6\u00D7c\t\u00B7\u00DE-o?\u00EAh\u00DE\u00C08K*uu\u00F9)\u00C4\u00A55[\u00A65\u00E1\u0095\u0094\x1E^,\u00CB\u00BET$c\u00F6\x16`\u00898\u00E0J\x02\u008A\u00D6\x14M\u009B\t|o\u0096Y\x000H\u00C7\\\x17\u00D8\u00BBg\u00C4\x05\x17m_{\u00F6Y\u00F3\x0F\x1E\u00B8\u00E0\u00E0\x07\x1E\u00F2\u0088s\u00DE\u00FF\u00DC\x7Fz\u00F6\u009F]\u00FCP\u00B9\u00F1\u00F6\x7F\u00BAg\u00C6\u00DF6\u00CE(\u0084;\u00C9\u00F8\u00C4!\u00BD\u00F0w\u00FE\u00E8\u00E6\u0087\u00BF\u00E7\u00A3\u00FA\u00B0\u009B\u00BE\u00D8\x1E|\u00F0\u00E8\u00F8\u00A0qs\u00FD\x01_:\u0084\u008B\u00AE\u00F9\u00E2R\u0096\u00DB\u00EB\u00C0\u00B8\x11K\u00C8G\u00C6\u0080)\u00BD\x0Et_\x1C\u00D6\u00BAC\x19zo\x1C\x05\u008D\x05`\u00C4S\u00A3p\u00CFA<,\x19\u00E0\u00D4kXH\u00A2\u009EZ\x1C\u00A0\x18t\u0089\u00F9p\x14\u00E7\u009E\u00DFt\u00FF\u00DE\u00E1\u00DA=\u00B3\u00ADO\u009E\u00BDG\u00AF\u00D8\x7F`\u00FC\u00ABG>\u00BA\u00FF\u00E5\u00B7?\u00E7\u00A2\u008F\u00DE\u00EF\u00C1r\u00DD\x1D\u00F8\u00F8\u00CE\u008C/s\u009CQ\bw\u00F2q\u00D5\u00A8g\u00BD\u00FFS\u00F8\u00CA\u00F7\u00BD\u00EB\u00E6\u00FB\\\u00FD\u00B9\u00E5\u00C5\u009F\u00BFe\u00F3^\u0087\x0F\u00EE\u00B9\u00C7\u00F6\u0091\u00D9E}G\u00CE_.\x16\x07\x16\u0090s\u0097\u00DB\u00B3\u00BD[Gt\u00D8\u00DC\u009Ec\u00B15\u00C3r{\u0080.\x15:6\u00E8\u00C2\u00B3\x1A\u00EA\x1D\u008A\u00A0\u0098\r#\u00D6\u00D0\u00B1\u00BE\u00B6\u00C4\u00AE\u008D>\u00CEf\u008B[f\u00D0/\x0Em\u00FD\u00A6a\u00BE\u00BCa>\u00C7\u00B5{\u00F6}\u00F1\u009A\u008B\u00F6\u00AE_}\u00EF\u008B\u00CF\u00BA\u00EA\u00D2'\u009F\u00F3\u00D9\u00C7=\x11\u009F\u00B9\u00F0^r\u00E4\u008E~&g\u00C6\u00AD\x1F\u00FF\x1F\x00\u00F3\u00AA\r=e\u0082\u0081\x00\x00\x00\x00IEND\u00AEB`\u0082" ;
    this.slash = "/"
    var targetFolder = new Folder(Folder.userData.fullName +this.slash+ "Aescripts"+this.slash+"colorPicker");
    !targetFolder.exists && targetFolder.create();

    this.settingFile = new File(targetFolder.fullName + this.slash + "colorPicker.xml");
    if(!this.settingFile.exists){
            this.settingFile.open("w");
            this.settingFile.write("<setting></setting>");
            this.settingFile.close();
    }

    this.haveSetting = function(name){
            this.settingFile.open("r");
            var content = this.settingFile.read();
            this.settingFile.close();
            return content.toString().indexOf("<"+name+">") !=-1;
        }

    this.getSetting = function(name){
            this.settingFile.open("r");
            var xml= new XML( this.settingFile.read() );
            this.settingFile.close();
            return xml[name].toString();
        }

    this.getSettingAsBool = function(name){
            var result = this.getSetting(name);
            return  result == "true" ? true:false;
        }

    this.saveSetting = function(name,value){
            this.settingFile.open("r");
            var xml= new XML( this.settingFile.read() );
            this.settingFile.close();
            var isOk = true;
            try{
                xml[name] = value.toString();
            }catch(err){
                isOk = false;
            }
            this.settingFile.open("w");
            this.settingFile.write(xml);
            this.settingFile.close();
            return isOk;
        }

}
    $.global.colorPicker = colorPicker;
    return colorPicker;
})();
// ==================== |---------| ====================
// ==================== | version | ====================
// ==================== |---------| ====================

/**
 * Constructs a new version object
 * @class
 * @name DuVersion
 * @classdesc Informations about the version of an application.<br />
 * The version is in the form: Major.Minor.Patch#Build. Example: <code>17.0.0x557</code> or <code>17.0.0-Alpha1</code> or <code>17.4</code><br />
 * The only required part is the Major version.
 * @param {string} [version='1.0.0'] The version in the form Major.Minor.Patch
 * @property {string} fullVersion The complete version name
 * @property {string} versionString The Major.Minor part as a string
 * @property {float} version The Major.Minor part as a float
 * @property {int} major
 * @property {int} minor
 * @property {int} patch
 * @property {int} build The build is -1 if it cannot be parsed as an int, like "alpha".
 * @property {string} buildString The build as a string
 * @category DuESF
 */
function DuVersion( version )
{
	if (!isdef( version )) version = '';
	
	var reV = /^(\d+)\.?(\d*)\.?(\d*)\D?(\S*)/i;
    var v = version.match( reV );

	if (version == '' || !v)
	{
		this.fullVersion = '1.0.0';
		this.versionString = '1.0';
		this.version = 1;
		this.major = 1;
		this.minor = 0;
		this.patch = 0;
		this.build = -1;
		this.buildString = "";
		return;
	}

	if (!v[ 1 ]) v[ 1 ]  = '1';
	if (!v[ 2 ]) v[ 2 ]  = '0';
	if (!v[ 3 ]) v[ 3 ]  = '0';
	if (!v[ 4 ]) v[ 4 ]  = '';

	this.fullVersion = version;
	this.versionString = v[ 1 ] + '.' + v[ 2 ];
	this.version = parseFloat(this.versionString);
	if ( isNaN(this.version) ) this.version = parseFloat(v[ 1 ]);
	if ( isNaN(this.version) ) this.version = 0;
	this.major = parseInt( v[ 1 ] );
	if ( isNaN(this.major) ) this.major = 0;
	this.minor = parseInt( v[ 2 ] );
	if ( isNaN(this.minor) ) this.minor = 0;
	this.patch = parseInt( v[ 3 ] );
	if ( isNaN(this.patch) ) this.patch = 0;
	this.build = parseInt( v[ 4 ] );
	if ( isNaN(this.build) ) this.build = -1;
	this.buildString = v[ 4 ];
}

/**
 * Checks if this version is higher than another.
 * @memberof DuVersion
 * @param {DuVersion|string} otherVersion The version to compare with.
 * @return {bool} true if this version is more recent than otherVersion
*/
DuVersion.prototype.higherThan = function( otherVersion )
{
	return DuVersion.compare(this, otherVersion);
}

/**
 * Checks if this version is higher than or equals another.
 * @memberof DuVersion
 * @param {DuVersion|string} otherVersion The version to compare with.
 * @return {bool} true if this version is more recent than or the same as otherVersion
*/
DuVersion.prototype.atLeast = function( otherVersion )
{
	if ( this.equals(otherVersion) ) return true;
	return DuVersion.compare(this, otherVersion);
}

/**
 * Checks if this version is the same as another.
 * @memberof DuVersion
 * @param {DuVersion|string} otherVersion The version to compare with.
 * @return {bool} true if this version is the same
*/
DuVersion.prototype.equals = function( otherVersion )
{
	if (! (otherVersion instanceof DuVersion) ) otherVersion = new DuVersion( otherVersion );

	return this.fullVersion == otherVersion.fullVersion;
}

/**
 * Compares two versions of an application
 * @static
 * @memberof DuVersion
 * @param {string|DuVersion} vA - The first version
 * @param {string|DuVersion} [vB=DuESF.scriptVersion] - The other version
 * @return {boolean} True if vA is more recent than vB (strict, if they're equal it will return false)
*/
DuVersion.compare = function (vA, vB)
{
	vB = def(vB, DuESF.scriptVersion);

	if (! (vA instanceof DuVersion) ) vA = new DuVersion( vA );
	if (! (vB instanceof DuVersion) ) vB = new DuVersion( vB );

	//compare major version
	if ( vA.major > vB.major ) return true;
	if ( vA.major < vB.major ) return false;

	//minor
	if ( vA.minor > vB.minor ) return true;
	if ( vA.minor < vB.minor ) return false;

	//patch
	if ( vA.patch > vB.patch ) return true;
	if ( vA.patch < vB.patch ) return false;

	//build 
	if ( vA.build >= 0 && vB.build >= 0 )
	{
		if ( vA.build > vB.build ) return true;
		if ( vA.build < vB.build ) return false;
	}

	//build as a string
	if ( vA.buildString == vB.buildString ) return false;
	if ( vA.buildString == '' && vB.buildString != '') return true;
	if ( vA.buildString != '' && vB.buildString == '') return false;

	var vABuildString = vA.buildString.toLowerCase();
	var vBBuildString = vB.buildString.toLowerCase();

	if ( vABuildString.indexOf('rc') >= 0 )
	{
		if ( vBBuildString.indexOf('rc') < 0 ) return true;
		return vABuildString > vBBuildString ;
	}

	if ( vABuildString.indexOf('gamma') >= 0 )
	{
		if ( vBBuildString.indexOf('rc') >= 0 ) return false;
		if ( vBBuildString.indexOf('gamma') < 0 ) return true;
		return vABuildString > vBBuildString ;
	}

	if ( vABuildString.indexOf('beta') >= 0 )
	{
		if ( vBBuildString.indexOf('rc') >= 0 ) return false;
		if ( vBBuildString.indexOf('gamma') >= 0 ) return false;
		if ( vBBuildString.indexOf('beta') < 0 ) return true;
		return vABuildString > vBBuildString ;
	}

	if ( vABuildString.indexOf('alpha') >= 0 )
	{
		if ( vBBuildString.indexOf('rc') >= 0 ) return false;
		if ( vBBuildString.indexOf('gamma') >= 0 ) return false;
		if ( vBBuildString.indexOf('beta') >= 0 ) return false;
		if ( vBBuildString.indexOf('alpha') < 0 ) return true;
		return vABuildString > vBBuildString ;
	}

	return vABuildString > vBBuildString;
}

/**
 * Information got from an update query on a RxVersion server, as returned by {@link DuVersion.checkUpdate DuVersion.checkUpdate()}.
 * @typedef {Object} DuVersion~UpdateReply
 * @property {Boolean} update Whether this script needs an update.
 * @property {string} version The available version.
 * @property {string} name The name of the script.
 * @property {string} description A description of the version.
 * @property {string} downloadURL The link to download the current version.
 * @property {string} changelogURL The link to the changelog.
 * @property {string} donateURL The link to make a donation.
 * @property {string} date The date of the version, in the form "yyyy-MM-dd hh:mm:ss".
 * @property {string} message Information about success/errors.
 * @property {Boolean} success false if the query failed and the version could not be retrieved.
 * @property {Boolean} accepted false if the query was wrong or the server did not recognize it. Should always be true.

/**
 * Checks if a new version is available for the current script,<br/>
 * using the DuESF.rxVersionURL if it is set.
 * @returns {DuVersion~UpdateReply|null} An {@link DuVersion~UpdateReply} or null if the server could not be reached.
 */
DuVersion.checkUpdate = function()
{
	if ( DuESF.rxVersionURL == '' ) return null;
	var co = new Socket;
	co.encoding = 'UTF-8';
	// No need for more than 5 seconds for just checking updates.
	co.timeout = 5;
    var url = DuESF.rxVersionURL.replace('http://','').replace('https://','').split('/');
    var baseUrl = url.shift();
	var fullUrl = baseUrl  + ':80';
    if ( co.open( fullUrl ) )
    {
        // Build request
		var os;
		if (DuSystem.mac) os = 'mac';
        else os = 'win';

		var args = {
			"getVersion": "",
			"name": DuESF.scriptName,
			"version": DuESF.scriptVersion.fullVersion,
			"os": os,
			"osVersion": DuSystem.osVersion,
			"host": DuESF.host,
			"hostVersion": DuESF.hostVersion.fullVersion,
			"languageCode": i18n.getLocale()
		}
		if (DuESF.isPreRelease) args["prerelease"] = '';
        var request = DuURL.buildGET( baseUrl, url, args );
		var reply = '';

		DuDebug.log( "Version check - Sys Info\r\n" +
			"Script: " + DuESF.scriptName + '\r\n' +
			"Script Version: " + DuESF.scriptVersion.fullVersion + '\r\n' +
			"OS: " + os + '\r\n' +
			"OS Version: " + DuSystem.osVersion + '\r\n' +
			"Host: " + DuESF.host + '\r\n' +
			"Host Version: " + DuESF.hostVersion.fullVersion + '\r\n' +
			"Language Code: " + i18n.getLocale() + '\r\n\r\n' +
			"Request: " + request
			);

		if ( co.writeln( request ) ) {
			// Wait for the closing } or timeout
			// timeout in 2 seconds
			var now = new Date().getTime();
			var timeout = now + 2000;
			while ( (reply == '' || reply.lastIndexOf('}') < reply.indexOf('{') ) && now < timeout)
			{
				$.sleep(100);
				now = new Date().getTime();
				reply += co.read();
			}
    	}

		co.close();

		// Sanitize reply (strip http headers, and parse)
		DuDebug.log( "Version check - Reply\r\n" + reply );
		reply = reply.substring( reply.indexOf('{'), reply.lastIndexOf('}')+1 );

		try {
			reply = JSON.parse(reply);
		}
		catch (e) {
			if (DuESF.debug) alert(e);
		}
		
		return reply;
    }

	return null;
}// ==================== |--------| ====================
// ==================== | string | ====================
// ==================== |--------| ====================

/**
 * JavaScript String related methods
 * @namespace
 * @category DuESF
 */
var DuString = {};

/**
 * Counts the number of occurences of item in string
 * @memberof DuString
 * @param {string} string - The string where to count
 * @param {string} item - the string to search
 * @return {int} the number of occurences
 */
DuString.occurences = function(string, item) {
    return (string.split(item).length - 1);
}

/**
 * Parses the string as a boolean.<br />
 * The following strings are falsy:<br />
 * 'false', '0', '', 'null', 'undefined', 'NaN'.<br />
 * Note that any string consisiting only in any number of 0 will be falsy.
 * @memberof DuString
 * @param {string} string - The string to parse
 * @param {boolean} [caseSensitive=true] - When false, 'FALSE', 'nan', 'UNdefined'... for example will be falsy too.
 * @return {boolean} The resulting boolean
 */
DuString.parseBool = function(string, caseSensitive) {
    caseSensitive = def(caseSensitive, true);

    if (!caseSensitive) string = string.toLowerCase();
    var nan = 'NaN';
    if (!caseSensitive) nan = 'nan';

    // test if string is only zeroes
    var reZeroes = /^0+$/g;
    if (reZeroes.test(string)) return false;

    return string !== 'false' && string !== 'null' && string !== '' && string !== 'undefined' && string !== nan;
}

/**
 * Replaces <strong>all</strong> occurences of a substring by another and returns the new string.
 * @memberof DuString
 * @param {string}	string			- The original string
 * @param {string}	find			- The substring to replace
 * @param {string}	replace			- The new substring to insert
 * @param {boolean}	[caseSensitive=true]	- Optionnal. Do a case sensitive search of substring.
 * @return	{string}	The new string
 */
DuString.replace = function(string, find, replace, caseSensitive) {
    if (caseSensitive == undefined) caseSensitive = true;
    var re = new RegExp(DuRegExp.escape(find), caseSensitive ? 'g' : 'gi');
    return string.replace(re, replace);
}

/**
 * Replaces all occurences of "{#}" in the string by the args.
 * @memberof DuString
 * @param {string}	string			- The original string
 * @param {string[]|string}	args			- The arguments
 * @return {string}	The new string
 */
DuString.args = function(string, args) {
    args = def(args, []);
    if (typeof args === 'string' || args instanceof String) args = [args];

    var str = string;
    while (str.indexOf("{#}") !== -1) {
        // While there is stuff to format
        if (args.length < 1) break;
        var arg = args.shift(); // Take the first arg and remove it
        str = str.replace("{#}", arg);
    }
    return str;
}

/**
 * Checks if a string ends with a given suffix
 * @memberof DuString
 * @param {string}	str 	The string to check
 * @param {string}	suffix	The suffix
 * @return {bool}	Whether the string ends with the given suffix or not
 */
DuString.endsWith = function(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

/**
 * Checks if a string starts with a given prefix
 * @memberof DuString
 * @param {string}	str 	The string to check
 * @param {string}	suffix	The suffix
 * @return {bool}	Whether the string ends with the given suffix or not
 */
DuString.startsWith = function(str, prefix) {
    return str.indexOf(prefix) === 0;
}

/**
 * Generates a new unique string (numbered)
 * @param {string} newString	- The wanted new string
 * @param {string[]} stringList 	- The list of strings where the new one must be generateUnique
 * @param {boolean} [increment=true] - true to automatically increment the new name if it already ends with a digit
 * @param {boolean} [isFile=false] - when generating name for files, setting this to true will add the increment before the extension
 * @return {string}	The unique string, with a new number at the end if needed.
 */
DuString.generateUnique = function(newString, stringList, increment) {
    increment = def(increment, true);
    if (!increment) newString += ' ';

    //detect digits
    var reg = "( *)(\\d+)([.,]?\\d*)$";
    //clean input
    var regexClean = new RegExp(reg);
    newString = newString.replace(regexClean, "");
    //go!
    var regex = new RegExp(DuRegExp.escape(newString) + reg);
    //The greatest number found
    var greatestNumber = 0;
    //The number of digits for the number as string
    var numDigits = 0;
    var spaceString = "";
    for (var i = 0; i < stringList.length; i++) {
        var currentNumberMatch = stringList[i].match(regex);
        if (stringList[i] === newString && greatestNumber == 0) greatestNumber++;
        else if (currentNumberMatch !== null) {
            //if its a decimal number, keep only the integer part
            var numberAsString = currentNumberMatch[2];
            //convert to int
            var numberAsInt = parseInt(currentNumberMatch[2], 10);
            if (isNaN(numberAsInt)) continue;
            if (numberAsInt >= greatestNumber) {
                greatestNumber = numberAsInt + 1;
                spaceString = currentNumberMatch[1];
            }
            //check if there are zeroes before the number, count the digits
            if (numberAsInt.toString().length < numberAsString.length && numDigits < numberAsString.length) numDigits = numberAsString.length;
        }
    }

    //add leading 0 if needed
    if (greatestNumber > 0) {
        //convert to string with leading zeroes
        if (greatestNumber == 1) {
            greatestNumber++;
            spaceString = " ";
        }
        newString += spaceString;
        greatestNumber = DuNumber.toString(greatestNumber, numDigits);
        newString += greatestNumber;
    }

    if (!increment) newString = newString.substr(0, newString.length - 1);

    return newString;
}

/**
 * Returns a copy of the string without leading and trailing white spaces.
 * @param {string}	 str	- The string to trim
 * @return {String} The trimmed string
 */
DuString.trim = function(str) {
    return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}

/**
 * Returns a copy of the string without leading white spaces.
 * @param {string}	 str	- The string to trim
 * @return {String} The trimmed string
 */
DuString.leftTrim = function(str) {
    return str.replace(/^[\s\uFEFF\xA0]+/, '');
}

/**
 * Returns a copy of the string without trailing white spaces.
 * @param {string}	 str	- The string to trim
 * @return {String} The trimmed string
 */
DuString.rightTrim = function(str) {
    return str.replace(/[\s\uFEFF\xA0]+$/, '');
}

/**
 * Returns a copy of the string without leading and trailing white spaces, and without any new line, leaving only standard spaces.
 * @param {string}	 str	- The string to trim
 * @return {String} The trimmed string
 */
DuString.fullTrim = function(str) {
    var newStr = str.replace(/[\s\uFEFF\xA0]+/g, ' ');
    return DuString.trim(newStr);
}

/**
 * Returns a copy of the string without trailing white spaces and numbers.
 * @param {string}	 str	- The string to trim
 * @return {String} The trimmed string
 */
DuString.trimNumbers = function(str) {
	return str.replace(/[\s\uFEFF\xA0\d]*$/g, '');
}

/**
 * Converts a size in Bytes to a human-readable string with a fitting unit automatically chosen<br />
 * Note that the conversion uses 1024 Bytes per kB.
 * @param {int} size The size in Bytes
 * @return {string} The stringified size
 */
DuString.fromSize = function(size) {
    if (size < 1024) return size + " B";
    if (size < 1048576) return DuPath.sizeFromBytes(size, "kB") + " kB";
    if (size < 1073741824) return DuPath.sizeFromBytes(size, "MB") + " MB";
    if (size < 1099511627776) return DuPath.sizeFromBytes(size, "GB") + " GB";
    else return DuPath.sizeFromBytes(size, "TB") + " TB";
}

/**
 * Generates a camel case text from a snake case or standard one
 * @param {string} text The source text
 * @return {string} The camelCase version of the text
 */
DuString.toCamelCase = function(text) {
    text = text.split(" ");
    if (text.length == 0) return "";
    text[0] = DuString.unCapitalize(text[0]);
    for (var i = 1, n = text.length; i < n; i++) {
        text[i] = DuString.capitalize(text[i]);
    }
    return text.join("");
}

/**
 * Sets the first character of the text to be capital case if it's a letter.<br />
 * Note that the string is left trimmed first: any leading white space is removed.
 * @param {string} text The source text
 * @return {string} The new text
 */
DuString.capitalize = function(text) {
    var newText = DuString.leftTrim(text);
    var firstChar = newText.substring(0, 1);
    firstChar = firstChar.toUpperCase();
    return firstChar + newText.substring(1);
}

/**
 * Sets the first character of the text to be lower case if it's a letter.<br />
 * Note that the string is left trimmed first: any leading white space is removed.
 * @param {string} text The source text
 * @return {string} The new text
 */
DuString.unCapitalize = function(text) {
    var newText = DuString.leftTrim(text);
    var firstChar = newText.substring(0, 1);
    firstChar = firstChar.toLowerCase();
    return firstChar + newText.substring(1);
}// ==================== |------| ====================
// ==================== | file | ====================
// ==================== |------| ====================

/**
	* File related methods
	* @namespace
	* @category DuESF
*/
var DuFile = {};

/**
	* Checks if the given path exists
	* @param {string}	 [path]	- The file path
	* @return {boolean} True or false
*/
DuFile.exists = function(path)
{
	var file;
	if (path instanceof File) file = path;
	else file = new File(path);
	return file.exists;
}

/**
	* Reads the first line of a file and return its content
	* @param {File|string}	 file	- The file
	* @return {string} The content
*/
DuFile.readFirstLine = function(file)
{
    if (jstype(file) == 'string') file = new File(file);
	//open and parse file
	if (!file.open('r')) return '';
	var data = file.readln();
	file.close();
	return data;
}

/**
	* Reads a whole file and return its content
	* @param {File|string}	 file	- The file
	* @param {string}	 [encoding='UTF-8']	- The text encoding
	* @return {string} The content
*/
DuFile.read = function(file,encoding)
{
    if (jstype(file) == 'string') file = new File(file);
	encoding =  def(encoding, 'UTF-8');
	//open and parse file
	file.encoding = encoding;
	if (!file.open('r')) return '';
	var data = file.read();
	file.close();
	return data;
}

/**
	* Writes a text file
	* @param {File}	 file	- The file
	* @param {string}	 content	- The content to write
	* @param {boolean}	 [append=false]	- Appends instead of replacing
	* @param {string}	 [encoding='UTF-8']	- The text encoding
    * @return {boolean} true if the file has been correctly written
*/
DuFile.write = function(file, content, append, encoding)
{
	append = def(append, false);
	encoding = def(encoding, 'UTF-8');
	//open and parse file
	file.encoding = encoding;
	var mode = 'w';
	if (append) mode = 'a';
	
	try
	{
		if (!file.open(mode)) return false;
		file.write(content);
		file.close();
		return true;
	}
	catch (e)
	{
		DuDebug.throwError( "Can't write file" , 'DuFile.write', e);
	}
	
    return false;
}

/**
	* Writes a line in a text file
	* @param {File}	 file	- The file
	* @param {string}	 content	- The content to write
	* @param {boolean}	 [append=false]	- Appends instead of replacing
	* @param {string}	 [encoding='UTF-8']	- The text encoding
    * @return {boolean} true if the file has been correctly written
*/
DuFile.writeln = function(file, content, append, encoding)
{
	append = def(append, false);
	encoding = def(encoding, 'UTF-8');
	//open and parse file
	file.encoding = encoding;
	var mode = 'w';
	if (append) mode = 'a';
	
	try
	{
		if (!file.open(mode)) return false;
		file.writeln(content);
		file.close();
		return true;
	}
	catch (e)
	{
		DuDebug.throwError( "Can't write file" , 'DuFile.write', e);
	}
	
    return false;
}

/**
	* Parses a JSON file
	* @param {File}	 file	- The file
	* @return {Object|null} The content or null if the file couldn't be parsed
*/
DuFile.parseJSON = function(file)
{
	if (!(file instanceof File)) file = new File(file);
	if (!file.exists) return {};
	//open and parse file
	var json = DuFile.read(file);
	if (json == '') return null;
	var data = {};

	try { data = JSON.parse(json); } catch (e) { if (DuESF.debug) alert(e.description + '\n\nJSON DATA:\n\n' + json); }
	return data;
}

/**
	* Saves a js object to a JSON file
	* @param {Object} obj - The object to save
	* @param {File|string} file - The file or URI
	* @return {boolean} true if the file has been correctly written
*/
DuFile.saveJSON = function (obj,file)
{
	if (!(file instanceof File)) file = new File(file);
	var data = JSON.stringify(obj,null,4);
    return DuFile.write( file, data );
}

/**
	* Parses a CSV file
	* @param {File}	 file	- The file
	* @param {string}	 [delimiter=',']	- The delimiter used
	* @param {string}	 [textSeparator='"']	- The separator for texts
	* @return {string[]|null} The content (a two-dimensionnal Array) or null if the file couldn't be parsed
*/
DuFile.parseCSV = function(file,delimiter,textSeparator)
{
	if (!isdef( delimiter )) delimiter = ',';
	if (!isdef( textSeparator )) textSeparator = '"';
	var reQuoted = new RegExp("(" + textSeparator + ")((?:\\\\\\1|(?:(?!\\1).))*)\\1");
	//open and parse file
	var data = DuFile.read(file);
	if (data == '') return null;
	data = data.split('\n');
	for (var i = 0, num = data.length; i < num; i++)
	{
		//get quoted values
		var quotedValues = [];
		var line = data[i];
		var quoted = reQuoted.exec(line);
		while (quoted)
		{
			quotedValues.push(quoted[2]);
			line = line.replace(quoted[0],'%%');
			quoted = reQuoted.exec(line);
		}
		data[i] = line.split(delimiter);
		for (var j = 0, numJ = data[i].length; j < numJ; j++)
		{
			data[i][j] = DuString.trim(data[i][j]);
			if (data[i][j] == '%%')
			{
				data[i][j] = quotedValues.shift();
			}
		}
	}
	return data;
}

/**
	* Shows the default save file dialog and returns the file selected by the user.<r />
	* If the user ommits the extension, the default extension will be appended (Mac OS fix).
	* @param {string}	 prompt	- The prompt text, displayed if the dialog allows a prompt.
	* @param {string}	 [filter='']	- The file type filter (windows only)
	* @param {string}	 [defaultExtension='']	- The default extension
	* @return {File|null} The file or null if the user cancels
*/
DuFile.saveDialog = function(prompt, filter, defaultExtension)
{
	defaultExtension = def( defaultExtension, '' );

	var saveFile = File.saveDialog(prompt, filter);
	if (!saveFile) return null;

	if (saveFile.name.indexOf('.') < 1 && defaultExtension != '')
	{
		if (defaultExtension.indexOf('.') != 0) defaultExtension = '.' + defaultExtension;
		saveFile = new File(saveFile.parent.absoluteURI + '/' + saveFile.name + defaultExtension);
	}

	return saveFile;
}

/**
	* Gets the number of a frame from an file/image sequence<br />
	* The number must be right before the extension.
	* @param {string}	path	- The path of the frame
*/
DuFile.getSequenceNumber = function(path)
{
	var string_noExt = path.substring(0,path.lastIndexOf('.'));
	var numberi = 0;
	var namei = 0;
	for(var i = string_noExt.length-1;i>=0;i--)
	{
		if(isNaN(parseInt(string_noExt.charAt(i))))
		{
			numberi = i+1;
			break;
		}
	}
	number = string_noExt.substring(numberi,string_noExt.length);
	return number;
}

/**
 * Moves a file to a new location
 * @param {string|File} file The file
 * @param {string|File} newURI The new URI/Path (including file name)
 * @return {File|null} the new file object, or null if it could not be moved
 */
DuFile.move = function(file, newURI) {
	if (!(file instanceof File)) file = new File(file);

	if (!file.exists) return null;

	if (file.copy(newURI)) {
		file.remove();
		var f = new File(newURI);
		if (f.exists) return f;
		else return null;
	}
	return null;
}// ==================== |--------| ====================
// ==================== | regexp | ====================
// ==================== |--------| ====================

/**
	* JavaScript Regular Expression related methods
	* @namespace
	* @category DuESF
*/
var DuRegExp = {};

/**
	* Escape reg exp reserved characters from a string to build a regular expression compatible string
	* @memberof DuRegExp
	* @param {string}	string		- The string to escape
	* @return	{string}	The escaped string
*/
DuRegExp.escape = function (string)
{
	return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

/**
 * The set containing javascript symbols ( +, -, [, etc.). Useful when parsing javascript code.
 * @readonly
 */
DuRegExp.javascriptSymbols = "[\\s=!/*\\-+%()[\\]{};:.]";

/**
 * The set containing javascript symbols ( +, -, [, etc.) except the dot. Useful when parsing javascript code.
 * @readonly
 */
DuRegExp.javascriptSymbolsNoDot = "[\\s=!/*\\-+%()[\\]{};:]";

/**
 * The set containing authorized characters for javascript variable. Useful when parsing javascript code.
 * @readonly
 */
DuRegExp.javascriptVarChars = "[a-zA-Z0-9_]";// ==================== |--------| ====================
// ==================== | number | ====================
// ==================== |--------| ====================

/**
	* Number related methods
	* @namespace
	* @category DuESF
*/
var DuNumber = {};

/**
 * Converts a number to a string, adding optionnal leading zeroes
 * @param {Number} num	- The number
 * @param {int} numDigits 	- The number of digits in the string. Adds leading zeroes
 * @param {int} [base=10]	- The conversion base
 * @return {string}	The number as a string
 */
DuNumber.toString = function (num, numDigits, base)
{
	if (base == undefined) base = 10;
	var result = num.toString(base);
	while(numDigits > result.length)
	{
		result  = "0" + result ;
	}
	return result;
}
// ==================== |------| ====================
// ==================== | math | ====================
// ==================== |------| ====================

/**
 * Math related methods
 * @namespace
 * @category DuESF
 */
var DuMath = {};

/**
 * Enum for locations.
 * @readonly
 * @static
 * @enum {int}
 */
DuMath.Location = {
    TOP: 1,
    TOP_RIGHT: 2,
    RIGHT: 3,
    BOTTOM_RIGHT: 4,
    BOTTOM: 5,
    BOTTOM_LEFT: 6,
    LEFT: 7,
    TOP_LEFT: 8,
    CENTER: 0
}

/**
 * Checks if the given location is on the right side
 * @param {DuMath.Location} location The location
 * @returns {boolean}
 */
DuMath.isLocationRight = function(location) {
    if (location == DuMath.Location.TOP_RIGHT) return true;
    if (location == DuMath.Location.RIGHT) return true;
    if (location == DuMath.Location.BOTTOM_RIGHT) return true;
    return false;
}

/**
 * Checks if the given location is on the center on the horizontal axis
 * @param {DuMath.Location} location The location
 * @returns {boolean}
 */
 DuMath.isLocationHCenter = function(location) {
    if (location == DuMath.Location.TOP) return true;
    if (location == DuMath.Location.CENTER) return true;
    if (location == DuMath.Location.BOTTOM) return true;
    return false;
}

/**
 * Checks if the given location is on the left side
 * @param {DuMath.Location} location The location
 * @returns {boolean}
 */
 DuMath.isLocationLeft = function(location) {
    if (location == DuMath.Location.TOP_LEFT) return true;
    if (location == DuMath.Location.LEFT) return true;
    if (location == DuMath.Location.BOTTOM_LEFT) return true;
    return false;
}

/**
 * Checks if the given location is on the top side
 * @param {DuMath.Location} location The location
 * @returns {boolean}
 */
 DuMath.isLocationTop = function(location) {
    if (location == DuMath.Location.TOP) return true;
    if (location == DuMath.Location.TOP_RIGHT) return true;
    if (location == DuMath.Location.TOP_LEFT) return true;
    return false;
}

/**
 * Checks if the given location is on the center on the vertical axis
 * @param {DuMath.Location} location The location
 * @returns {boolean}
 */
 DuMath.isLocationVCenter = function(location) {
    if (location == DuMath.Location.LEFT) return true;
    if (location == DuMath.Location.CENTER) return true;
    if (location == DuMath.Location.RIGHT) return true;
    return false;
}

/**
 * Checks if the given location is on the bottom side
 * @param {DuMath.Location} location The location
 * @returns {boolean}
 */
 DuMath.isLocationBottom = function(location) {
    if (location == DuMath.Location.BOTTOM_RIGHT) return true;
    if (location == DuMath.Location.CENTER) return true;
    if (location == DuMath.Location.BOTTOM_LEFT) return true;
    return false;
}

/**
 * Generates a random integer between minimum and maximum
 * @memberof DuMath
 * @param {int}	[min=0]	- The minimum value
 * @param {int}	[max=1]	- The maximum value
 * @return {int}	The randomly generated integer
 */
DuMath.random = function(min, max) {
    if (min == undefined) min = 0;
    if (max == undefined) max = 1;
    var rng = null;
    $.sleep(10);
    var date = new Date();
    var rng = new Math.seedrandom(date.getTime())();
    //rng = Math.random();

    if (!rng) return 0;
    return rng * (max - min) + min;
}

/**
 * Generates a random integer between minimum and maximum.<br/>
 * The results are distributed along a gaussian (bell) curve.<br/>
 * Note that a few (< 10%) values may be outside of the range. Set the <code>bounded</code> to true to avoid that.
 * @memberof DuMath
 * @param {int}	[min=0]	- The minimum value
 * @param {int}	[max=1]	- The maximum value
 * @param {Bool} [bounded=false] - When this is false, a few values may be outside the range. Set it to true to make sure all values are between min and max.
 * @return {int}	The randomly generated integer
 */
DuMath.gaussRandom = function(min, max, bounded) {
    min = def(min, 0);
    max = def(max, 1);
    bounded = def(bounded, false);

    var u = 0, v = 0;
    while(u === 0) u = DuMath.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = DuMath.random();
    var num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
    num = num / 10.0 + 0.5; // Translate to 0 -> 1
    if (bounded && (num > 1 || num < 0)) return DuMath.gaussRandom(min, max, true); // resample between 0 and 1
    num *= max - min; // Stretch to fill range
    num += min; // offset to min
    return num;
}

/**
 * Measures the vector length between two points
 * @param {int[]} value1 - The first value
 * @param {int[]} value2 - The second value
 * @return {float} The length
 */
DuMath.length = function(value1, value2) {
    if (typeof value1 !== typeof value2) {
        return null;
    }
    if (value1.length > 0) {
        var result = 0;
        for (var dim = 0; dim < value1.length; dim++) {
            result += (value1[dim] - value2[dim]) * (value1[dim] - value2[dim]);
        }
        result = Math.sqrt(result);
        return result;
    } else return Math.abs(value1 - value2);
}

/**
 * Compares two numbers
 * @memberof DuMath
 * @param {Number|Number[]} value1 - The first value
 * @param {Number|Number[]} value2 - The second value
 * @param {int} [floatPrecision=-1] - The precision for (float) number comparison, number of decimals. Set to -1 to not use.
 * @return {boolean} true if the two values are equal
 */
DuMath.equals = function(value1, value2, floatPrecision, test) {
    floatPrecision = def(floatPrecision, -1);
    value1 = new DuList(value1);
    value2 = new DuList(value2);

    if (value1.length() != value2.length()) return false;

    var item1 = value1.next();
    while(item1 !== null)
    {
        var item2 = value2.at(value1.current);
        if (jstype(item1) !== jstype(item2)) return false;
        if (jstype(item1) === 'array') return DuMath.equals(item1, item2, floatPrecision);
        if (jstype(item1) !== 'number') return false;

        if (floatPrecision >= 0) {
            var mul = Math.pow(10, floatPrecision);
            var num1 = Math.round(item1 * mul) / mul;
            var num2 = Math.round(item2 * mul) / mul;
            if (num1 != num2) return false;
        } else  if (item1 != item2) return false;

        item1 = value1.next();
    }

    return true;
}

/**
 * Calculates the log10 of a number
 * @memberof DuMath
 * @param {float}	w	- The number
 * @return {float}	The result of log10(x)<br />
 * i.e. Math.log(x)/Math.LN10
 */
DuMath.log10 = function(x) {
    return Math.log(x) / Math.LN10;
}

/**
 * Calculates the average value in a list
 * @memberof DuMath
 * @param {Number|Number[]}	values	- values
 * @return {float}	The average value
 */
DuMath.average = function(values) {
    if (!(values instanceof Array)) return values;
    var sum = 0;
    var total = values.length;
    if (total == 0) return 0;
    for (var i = 0; i < total; i++) {
        sum += values[i];
    }
    return sum / total;
}

/**
 * Alias for {@link DuMath.average}.
 * @memberof DuMath
 * @function
 */
DuMath.mean = DuMath.average;

/**
 * Clamps the value
 * @memberof DuMath
 * @param {Number|Number[]}	values	- values
 * @param {Number}	[min=0] - The minimum value
 * @param {Number}	[max=1] - The maximum value
 * @return {Number|Number[]}	The clamped values
 */
DuMath.clamp = function(values, min, max) {
    if (!isdef( min )) min = 0;
    if (!isdef( max )) max = 1;

    if (max < min) {
        var t = max;
        max = min;
        min = t;
    }

    var isArray = true;
    var result = [];
    if (!(values instanceof Array)) {
        isArray = false;
        values = [values];
    }
    for (var i = 0, num = values.length; i < num; i++) {
        var v = values[i];
        if (v < min) v = min;
        if (v > max) v = max;
        result.push(v);
    }
    if (isArray) return result;
    else return result[0];
}

/**
 * Converts the number from degrees to radians
 * @memberof DuMath
 * @param {number} value - the value
 * @return {float} The value in radians
 */
DuMath.toRadians = function(value) {
    return value * Math.PI / 180;
}

/**
 * Converts the number from radians to degrees
 * @memberof DuMath
 * @param {number} value - the value
 * @return {float} The value in degrees
 */
DuMath.toDegrees = function(value) {
    return value * 180 / Math.PI;
}

/**
 * The logistic function (sigmoid)
 * @param {Number} value The value
 * @param {Number} [midValue=0] The midpoint value, at which the function returns max/2
 * @param {Number} [min=0] The minimum return value
 * @param {Number} [max=1] The maximum return value
 * @param {Number} [rate=1] The logistic growth rate or steepness of the function
 * @return {Number} The result in the range [min, max] (excluding min and max)
 */
DuMath.logistic = function(value, midValue, min, max, rate) {
    if (!isdef( midValue )) midValue = 0;
    if (!isdef( min )) min = 0;
    if (!isdef( max )) max = 1;
    if (!isdef( rate )) rate = 1;
    var exp = -rate * (value - midValue);
    var result = 1 / (1 + Math.pow(Math.E, exp));
    return result * (max - min) + min;
}

/**
 * The inverse logistic function (inverse sigmoid)
 * @param {Number} v The variable
 * @param {Number} [midValue=0] The midpoint value, at which the function returns max/2 in the original logistic function
 * @param {Number} [min=0] The minimum return value of the original logistic function
 * @param {Number} [max=1] The maximum return value of the original logistic function
 * @param {Number} [rate=1] The logistic growth rate or steepness of the original logistic function
 * @return {Number} The result
 */
DuMath.inverseLogistic = function(v, midValue, min, max, rate) {
    if (!isdef( midValue )) midValue = 0;
    if (!isdef( max )) max = 1;
    if (!isdef( min )) min = 0;
    if (!isdef( rate )) rate = 1;

    if (v == min) return 0;

    return midValue - Math.log((max - min) / (v - min) - 1) / rate;
}

/**
 * The gaussian function
 * @param {Number} value The variable
 * @param {Number} [min=0] The minimum return value
 * @param {Number} [max=1] The maximum return value
 * @param {Number} [center=0] The center of the peak
 * @param {Number} [fwhm=1] The full width at half maximum of the curve
 * @return {Number} The result
 */
DuMath.gaussian = function(value, min, max, center, fwhm) {
    if (!isdef( max )) max = 1;
    if (!isdef( min )) min = 0;
    if (!isdef( center )) center = 0;
    if (!isdef( fwhm )) fwhm = 1;
    if (fwhm === 0 && value == center) return max;
    else if (fwhm === 0) return 0;

    var exp = -4 * Math.LN2;
    exp *= Math.pow((value - center), 2);
    exp *= 1 / Math.pow(fwhm, 2);
    var result = Math.pow(Math.E, exp);
    return result * (max - min) + min;
}

/**
 * A "reversed" gaussian function, growing faster with low value
 * @param {Number} value The variable
 * @param {Number} [min=0] The minimum return value
 * @param {Number} [max=1] The maximum return value
 * @param {Number} [center=0] The center of the peak
 * @param {Number} [fwhm=1] The full width at half maximum of the curve
 * @return {Number} The result
 */
DuMath.reversedGaussian = function(value, min, max, center, fwhm) {
    r = -value - fwhm + 1;
    return gaussian(value, min, max, center, r);
}

/**
 * The inverse gaussian function
 * @param {Number} v The variable
 * @param {Number} [min=0] The minimum return value of the corresponding gaussian function
 * @param {Number} [max=1] The maximum return value of the corresponding gaussian function
 * @param {Number} [center=0] The center of the peak of the corresponding gaussian function
 * @param {Number} [fwhm=1] The full width at half maximum of the curve of the corresponding gaussian function
 * @return {Number[]} The two possible results, the lower is the first in the list. If both are the same, it is the maximum
 */
DuMath.inverseGaussian = function(v, min, max, center, fwhm) {
    if (!isdef( max )) max = 1;
    if (!isdef( min )) min = 0;
    if (!isdef( center )) center = 0;
    if (!isdef( fwhm )) fwhm = 1;
    if (v == 1) return [center, center];
    if (v === 0) return [center + fwhm / 2, center - fwhm / 2];
    if (fwhm === 0) return [center, center];

    var result = (v - min) / (max - min);
    result = Math.log(result) * Math.pow(fwhm, 2);
    result = result / (-4 * Math.LN2);
    result = Math.sqrt(result);
    return [result + center, -result + center];
}

/**
 * The inverse of the reversed gaussian function
 * @param {Number} value The variable
 * @param {Number} [min=0] The minimum return value of the corresponding gaussian function
 * @param {Number} [max=1] The maximum return value of the corresponding gaussian function
 * @param {Number} [center=0] The center of the peak of the corresponding gaussian function
 * @param {Number} [fwhm=1] The full width at half maximum of the curve of the corresponding gaussian function
 * @return {Number[]} The two possible results, the lower is the first in the list. If both are the same, it is the maximum
 */
DuMath.inverseReversedGaussian = function(value, min, max, center, fwhm) {
    r = -value - fwhm + 1;
    return inverseGaussian(value, min, max, center, r);
}

/**
 * The linear function
 * @param {Number} value The variable
 * @param {Number} [min=0] The minimum input value
 * @param {Number} [max=1] The maximum input value
 * @param {Number} [targetMin=0] The minimum output value
 * @param {Number} [targetMax=1] The maximum output value
 * @param {Boolean} [clamp=false] Whether to clamp the output value to the target or not.
 * @returns {Number}
 */
DuMath.linear = function(value, min, max, targetMin, targetMax, clamp) {
    min = def(min, 0);
    max = def(max, 1);
    targetMin = def(targetMin, 0);
    targetMax = def(targetMax, 1);
    clamp = def(clamp, false);

    // Handle stupid values
    if (min == targetMin && max == targetMax) return value;
    if (min == max) return value;
    if (targetMin == targetMax) return targetMin;
    if (value == min) return targetMin;
    if (value == max) return targetMax;

    var result = targetMin + (value - min) / (max - min) * (targetMax - targetMin);
    if (clamp) result = DuMath.clamp(value, targetMin, targetMax);
    return result;
}

/**
 * Checks if a point is located inside given bounds
 * @param {float[]} point The point
 * @param {float[]} bounds The bounds. The number of bounds must be at least twice the number of dimensions, in this order : [a1, a2, ..., x1,x2,y1,y2,z1,z2]
 * @return {Boolean} true if the point is inside the bounds
 */
DuMath.isInside = function(point, bounds) {
    for (var i = 0, n = point.length; i < n; i++) {
        var c = point[i];
        var c1 = bounds[i * 2];
        var c2 = bounds[i * 2 + 1];

        if (c < c1) return false;
        if (c > c2) return false;
    }
    return true;
}

/**
 * Checks the sign of a number
 * @param {Number} num The number to check
 * @returns {int} 1 if num is positive, -1 if negative, 0 in other cases (0, NaN...)
 */
DuMath.sign = function(num) {
    if (num < 0) return -1;
    if (num > 0) return 1;
    return 0;
}

/**
 * Gets the bounds of the values
 * @param {Number[][]} values A list of values
 * @return {float[]} The bounds, for N dimensions: [min1, min2, ..., minN, max1, max2, ..., maxN]
 */
DuMath.bounds = function(values) {
    var mins = [];
    var maxs = [];

    for (var i = 0, n = values.length; i < n; i++) {
        var v = values[i];
        for (var j = 0, d = v.length; j < d; j++) {
            if (typeof(mins[j]) === 'undefined') mins[j] = v[j];
            else if (v[j] < mins[j]) mins[j] = v[j];

            if (typeof(maxs[j]) === 'undefined') maxs[j] = v[j];
            else if (v[j] > maxs[j]) maxs[j] = v[j];
        }
    }

    return mins.concat(maxs);
}

/**
 * Checks if the direction changes (if the point is at an extreme value). If the values have multiple dimensions, checks each axis individually.
 * @param {Number|Number[]} previousValue The value just before the point.
 * @param {Number|Number[]} point The point to check.
 * @param {Number|Number[]} nextValue The value just after the point.
 * @param {int} [precision=1] The precision for floating point comparisons; number of decimals.
 * @return {Boolean} 
 */
DuMath.isExtremePoint = function(previousValue, point, nextValue, precision) {
    previousValue = new DuList(previousValue);
    point = new DuList(point);
    nextValue = new DuList(nextValue);

    for (var i = 0, n = point.length(); i < n; i++) {
		var p = point.at(i);
		var pv = previousValue.at(i);
		var nx = nextValue.at(i);
		if (DuMath.equals(p, pv, 1) && DuMath.equals(p, nx, 1)) continue;
        // Get the dif
        var pDif = p-pv;
        var nDif = p-nx;
        // If the sign is the same, this is an extreme
        if (pDif >= 0 && nDif >= 0) return true;
        if (pDif <= 0 && nDif <= 0) return true;
		// If one of the two is 0, this is an extreme
        if (DuMath.equals(pDif, 0.0, 1)) return true;
        if (DuMath.equals(nDif, 0.0, 1)) return true;
    }
	return false;
}

/**
 * Checks if the point is an inflexion point.
 * @param {Number|Number[]} previousVelocity The derivative (speed) of a point just before the point.
 * @param {Number|Number[]} pointVelocity The derivative (speed) at the point to check.
 * @param {Number|Number[]} nextVelocity The derivative (speed) of a point just after the point.
 * @return {Boolean} 
 */
DuMath.isInflexionPoint = function(previousVelocity, pointVelocity, nextVelocity) {
    previousVelocity = new DuList(previousVelocity);
    pointVelocity = new DuList(pointVelocity);
    nextVelocity = new DuList(nextVelocity);

    for (var i = 0, n = pointVelocity.length(); i < n; i++) {
		var p = pointVelocity.at(i);
		var pv = previousVelocity.at(i);
		var nx = nextVelocity.at(i);
		if (p == pv && p == nx) continue;
		if ( p >= pv && p >= nx) return true;
		if ( p <= pv && p <= nx) return true;
    }
	return false;
}// ==================== |---------------| ====================
// ==================== | interpolation | ====================
// ==================== |---------------| ====================

/**
 * Interpolation methods
 * @namespace
 * @category DuESF
 */
var DuInterpolation = {};

/**
 * Linear (extra/inter)polation
 * @param {Number} value The variable
 * @param {Number} [min=0] The minimum input value
 * @param {Number} [max=1] The maximum input value
 * @param {Number} [targetMin=0] The minimum output value
 * @param {Number} [targetMax=1] The maximum output value
 * @param {Boolean} [extrapolate=true] Whether to extrapolate outside the target.
 * @returns {Number}
 */
DuInterpolation.linear = function(value, min, max, targetMin, targetMax, extrapolate) { 
    extrapolate = def(extrapolate, true);
    return DuMath.linear(value, min, max, targetMin, targetMax, !extrapolate)
};

/**
 * Interpolates a value with a bezier curve.
 * @param {number} t The value to interpolate
 * @param {number} [tMin=0] The minimum value of the initial range
 * @param {number} [tMax=1] The maximum value of the initial range
 * @param {number} [value1=0] The minimum value of the interpolated result
 * @param {number} [value2=1] The maximum value of the interpolated result
 * @param {number[]} [bezierPoints=[0.33,0.0,0.66,1.0]] an Array of 4 coordinates wihtin the [0.0, 1.0] range which describes the Bézier interpolation. The default mimics the native ease() function<br />
 * [ outTangentX, outTangentY, inTangentX, inTangentY ]
 * @return {number} the value.
 */
DuInterpolation.bezier = function(t, tMin, tMax, value1, value2, bezierPoints) {
    tMin = def(tMin, 0);
    tMax = def(tMax, 1);
    value1 = def(value1, 0);
    value2 = def(value2, 1);
    bezierPoints = def(bezierPoints, [0.33,0.0,0.66,1.0]);

    if (arguments.length !== 5 && arguments.length !== 6) return (value1+value2)/2;
    var a = value2 - value1;
    var b = tMax - tMin;
    if (b == 0) return (value1+value2)/2;
    var c = DuMath.clamp((t - tMin) / b, 0, 1);
    if (!(bezierPoints instanceof Array) || bezierPoints.length !== 4) bezierPoints = [0.33,0.0,0.66,1];
    return a * h(c, bezierPoints) + value1;

    function h(f, g) {
        var x = 3 * g[0];
        var j = 3 * (g[2] - g[0]) - x;
        var k = 1 - x - j;
        var l = 3 * g[1];
        var m = 3 * (g[3] - g[1]) - l;
        var n = 1 - l - m;
        var d = f;
        for (var i = 0; i < 5; i++) {
            var z = d * (x + d * (j + d * k)) - f;
            if (Math.abs(z) < 1e-3) break;
            d -= z / (x + d * (2 * j + 3 * k * d));
        }
        return d * (l + d * (m + d * n));
    }
}

/**
 * Interpolates and extrapolates a value with an exponential function.
 * @param {number} t The value to interpolate
 * @param {number} [tMin=0] The minimum value of the initial range
 * @param {number} [tMax=1] The maximum value of the initial range
 * @param {number} [value1=0] The minimum value of the interpolated result
 * @param {number} [value2=1] The maximum value of the interpolated result
 * @param {number} [rate=1] The raising speed in the range [0, inf].
 * @return {number} the value.
 */
DuInterpolation.exponential = function(t, tMin, tMax, vMin, vMax, rate)
{
	tMin = def(tMin, 0);
    tMax = def(tMax, 1);
    vMin = def(vMin, 0);
    vMax = def(vMax, 1);
    rate = def(rate, 1);
   
    if (rate == 0) return DuInterpolation.linear(t, tMin, tMax, vMin, vMax);

    // handle tMax < tMin
    var rev = false;
    if (tMax < tMin) {
        var temp = tMax;
        tMax = tMin;
        tMin = temp;
        rev = true;
    }

	// Offset t to be in the range 0-Max
	tMax = ( tMax - tMin ) * rate;
	t = ( t - tMin ) * rate;
	// Compute the max
	var m = Math.exp(tMax);
	// Compute current value
	t = Math.exp(t);
	var result = DuInterpolation.linear(t, 1, m, vMin, vMax);
    if (rev) result = DuInterpolation.linear(result, vMin, vMax, vMax, vMin);
    return result;
}

/**
 * Interpolates a value with a gaussian function.
 * @function
 * @param {number} t The value to interpolate
 * @param {number} [tMin=0] The minimum value of the initial range
 * @param {number} [tMax=1] The maximum value of the initial range
 * @param {number} [value1=0] The minimum value of the interpolated result
 * @param {number} [value2=1] The maximum value of the interpolated result
 * @param {number} [rate=0] The raising speed in the range [-1.0, 1.0].
 * @return {number} the value.
 */
DuInterpolation.gaussian = function( t, tMin, tMax, value1, value2, rate )
{
    tMin = def(tMin, 0);
    tMax = def(tMax, 1);
    value1 = def(value1, 0);
    value2 = def(value2, 1);
    rate = def(rate, 0);

    // handle tMax < tMin
    var rev = false;
    if (tMax < tMin) {
        var temp = tMax;
        tMax = tMin;
        tMin = temp;
        rev = true;
    }

    // fix small bump at first value
    if (t != tMin)
    {
        var newValue1 = DuInterpolation.gaussian( tMin, tMin, tMax, value1, value2, rate );
        var offset = newValue1 - value1;
        value1 = value1 - offset;
    }
	if (rate < 0) rate = rate*10;
	rate = DuInterpolation.linear(t, tMin, tMax, 0.25, rate);
	var r = ( 1 - rate );
    var fwhm = (tMax-tMin) * r;
    var center = tMax;
	if (t >= tMax) {
        if (rev) return value1;
        return value2;
    }
    if (fwhm === 0 && t == center) {
        if (rev) return value1;
        return value2;
    }
    else if (fwhm === 0) {
        if (rev) return value2;
        return value1;
    }
	
    var exp = -4 * Math.LN2;
    exp *= Math.pow((t - center),2);
    exp *= 1/ Math.pow(fwhm, 2);
    var result = Math.pow(Math.E, exp);
	result = result * (value2-value1) + value1;

    if (rev) result = DuInterpolation.linear( result, value1, value2, value2, value1);

    return result;
}

/**
 * Interpolates and extrapolates a value with a logarithmic function.
 * @function
 * @param {number} t The value to interpolate
 * @param {number} [tMin=0] The minimum value of the initial range
 * @param {number} [tMax=1] The maximum value of the initial range
 * @param {number} [value1=0] The minimum value of the interpolated result
 * @param {number} [value2=1] The maximum value of the interpolated result
 * @param {number} [rate=1] The raising speed in the range [0, inf].
 * @return {number} the value.
 */
DuInterpolation.logarithmic = function(t, tMin, tMax, vMin, vMax, rate)
{
    tMin = def(tMin, 0);
    tMax = def(tMax, 1);
    vMin = def(vMin, 0);
    vMax = def(vMax, 1);
    rate = def(rate, 1);
    
    if (rate == 0) return DuInterpolation.linear(t, tMin, tMax, vMin, vMax);

    // handle tMax < tMin
    var rev = false;
    if (tMax < tMin) {
        var temp = tMax;
        tMax = tMin;
        tMin = temp;
        rev = true;
    }

    // Offset t to be in the range 0-Max
    tMax = ( tMax - tMin ) * rate + 1;
    t = ( t - tMin ) * rate + 1;
    if (t <= 1) {
        if (rev) return vMax;
        return vMin;
    } 
    // Compute the max
    var m = Math.log(tMax);
    // Compute current value
    var v = Math.log(t);
    var result = DuInterpolation.linear(v, 0, m, vMin, vMax);
    if (rev) result = DuInterpolation.linear(result, vMin, vMax, vMax, vMin);
    return result;
}

/**
 * Interpolates and extrapolates a value with a logistic (sigmoid) function.
 * @function
 * @param {number} t The value to interpolate
 * @param {number} [tMin=0] The minimum value of the initial range
 * @param {number} [tMax=1] The maximum value of the initial range
 * @param {number} [value1=0] The minimum value of the interpolated result
 * @param {number} [value2=1] The maximum value of the interpolated result
 * @param {number} [rate=1] The raising speed in the range [0, inf].
 * @param {number} [tMid] The t value at which the interpolated value should be half way. By default, (tMin+tMax)/2
 * @return {number} the value
 */
DuInterpolation.logistic = function( t, tMin, tMax, value1, value2, rate, tMid )
{
    tMin = def(tMin, 0);
    tMax = def(tMax, 1);
    value1 = def(value1, 0);
    value2 = def(value2, 1);
    rate = def(rate, 1);
    tMid = def(tMid, (tMin+tMax)/2);

    if (rate == 0) return DuInterpolation.linear(t, tMin, tMax, value1, value2);
    t = DuMath.logistic( t, tMid, tMin, tMax, rate);
    
    // Scale to actual min/max
    var m = DuMath.logistic( tMin, tMid, tMin, tMax, rate);
    var M = DuMath.logistic( tMax, tMid, tMin, tMax, rate);

    return DuInterpolation.linear( t, m, M, value1, value2);
}
// ==================== |------| ====================
// ==================== | date | ====================
// ==================== |------| ====================

/**
	* Date related methods
	* @namespace
	* @category DuESF
*/
var DuDate = {};

// low-level undocumented list. Use getMonth and getMonthName
DuDate.MonthNames = [
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December",
];

/**
	* Gets the month number from a literal localized name. 0 is january, 11 is december
	* @memberof DuDate
	* @param {string} string - The month name
	* @return {int} The month number
*/
DuDate.getMonth = function (string)
{
	string = string.toLowerCase();
	for (var i = 0; i < 12; i++)
	{
		if ( DuDate.MonthNames[i].toLowerCase().indexOf(string) >= 0 ) return i;
	}

	return 0;
}

/**
	* Gets the month name from an index. 0 is january, 11 is december
	* @memberof DuDate
	* @param {int} string - The month index
	* @return {string} The month name
*/
DuDate.getMonthName = function (index)
{
	if (index >= 0 && index < 12) return i18n._(DuDate.MonthNames[i]);

	return "";
}

/**
	* Returns a pretty formatted string representing the date
	* @todo format option
	* @memberof DuDate
	* @param {Date} date - The date
	* @return {string} The date
*/
DuDate.toString = function (date)
{
	var hour = date.getHours();
	var minute = date.getMinutes();
	var second = date.getSeconds();
	var day = date.getDate();
	var month = date.getMonth();
	var year = date.getFullYear();

	return year + "-" + (month+1) + "-" + day + " " + hour + "-" + minute + "-" + second;
}// ==================== |--------| ====================
// ==================== | binary | ====================
// ==================== |--------| ====================

/**
 * Constructs a new string representation of a binary file
 * @class DuBinary
 * @classdesc Represents a binary file as a string and a filename.
 * @param	{string}	binAsString			- The string representation.
 * @param	{string}	fileName			- The name of the original file.
 * @param	{string}	[category='']			- A Category for the file, will be used as a subfolder to extract files. Can have subcategories like "category/subcategory".
 * @category DuESF
 */
function DuBinary( binAsString, fileName, category)
{
    /**
        * The string representation of the binary file.
        * @memberof DuBinary
        * @type {string}
        * @name binAsString
        */
	this.binAsString = binAsString;

	fileName = DuString.replace(fileName,'%20','_');
    /**
        * The name of the original file.
        * @memberof DuBinary
        * @type {string}
        * @name fileName
        */
	this.fileName = fileName;

	if (category === undefined) category = '';
    /**
        * A Category for the file, will be used as a subfolder to extract files. Can have subcategories like "category/subcategory".
        * @memberof DuBinary
        * @type {string}
        * @name category
        */
	this.category = category;
}

/**
 * Returns a string representation of the {@link DuBinary} which can be written in a jsxinc file.
 * @returns {string} The source.
 */
DuBinary.prototype.toSource = function ()
{
    return 'new DuBinary( "' + this.binAsString + '", "' + this.fileName + '", "' + this.category + '" )';
}

/**
 * Writes the file.
 * @param {string}			[outputFileName=DuFolder.duesfData/category/binaryfilename]	- The output filename.
 * @param {boolean}			[onlyAtFirstRun=true]	- Does not extract the file if it already exists and this is not the first time this version of the script is being used.<br />
 * The file will be extracted only if the script is new or has just been updated (the version changed).
 * @return {File|null} The file created, null if the file could not be written.<br />
 * If the file is not written, check user permissions, and check if the file and network access preference is checked.
 * @example
 * //First, include the text representation of the file (Add a # before the include word)
 * include executable.exe.jsxinc
 * // Now, a variable called `executable` (the original file name without extension) is available, it's an instance of DuBinary.
 * var execFile = executable.extract();
 * //Now, the file exists in the file system, and execFile is an ExtendScript File object.
 * // The `DuBinary.extract()` method extracts the file to the Application Data folder by default.
 * execFile.fsName; // C:\users\duduf\appData\Roaming\RxMaboratory\AdobeScripts\DuESF\icon.png (Example on Windows)
 * @example
 * //You can specify the output file name. (Add a # before the include word)
 * include preset.ffx.jsxinc
 * var presetFile = preset.extract("C:/test/test_preset.ffx");
 * presetFile.fsName; // C:\test\test_preset.exe");
 */
DuBinary.prototype.toFile = function( outputFileName, onlyAtFirstRun )
{
    onlyAtFirstRun = def( onlyAtFirstRun, true );

    var rootFolder = DuFolder.duesfData.absoluteURI;
    outputFileName = def( outputFileName, rootFolder + '/' + this.category + '/' + this.fileName )

    var file = new File(outputFileName);

    // performance
    // do not extract if the file exists and we're not in the first run (or debug mode)
    if (file.exists && !DuESF.scriptFirstRun && !DuESF.debug && onlyAtFirstRun)
    {
        return file;
    }

	if (!file.exists)
	{
		var folder = new Folder(file.path);
		if (!folder.exists)
		{
			folder.create();
		}
	}

    DuFile.write( file, this.binAsString, false, 'BINARY');

    return file;
}

/**
 * Writes the DuBinary to a jsxinc file
 * @param {string} outputFilePath  - The file path for the output.
 * @param {any} [varName=File name without extension]	- The name of the variable used to store the javascript object.
 * @returns {File} the new jsxinc file.
 */
DuBinary.prototype.toJsxincFile = function( outputFilePath, varName )
{
    var fileName = DuPath.getBasename(outputFilePath);
    varName = def (varName, fileName.replace(/\..+/,'') );
	varName = DuString.replace(varName,'%20','_');

    var jsString = 'var ' + varName + ' = ' + this.toSource() + ';\r\n' + varName + ';\r\n';

	var outputFile = new File( outputFilePath );
	outputFile.encoding = 'UTF8';
	if (outputFile.open("w", "TEXT", "????"))
	{
		outputFile.write(jsString);
		outputFile.close();
	}
    
    return outputFile;
}
// =============== STATIC =====================

/**
 * Creates a {@link DuBinary} object from an existing file
 * @static
 * @param {File}    file      - The File to convert
 * @param {string}  [category]  - A Category for the file, will be used as a subfolder to extract files. Can have subcategories like "category/subcategory".
 * @return {DuBinary} The {@link DuBinary} object containing the file as a string
 */
DuBinary.fromFile = function ( file, category )
{
    var content = '';

    file.encoding = "BINARY";
    if (file.open("r"))
    {
        content = file.read().toSource();
        content = content.substr(content.indexOf("\"")+1, content.length);
        content = content.substr(0, content.lastIndexOf("\""));
        file.close();
    }

    return new DuBinary(content,file.name,category);
}

/**
 * Converts a file to a jsxinc file
 * @static
 * @param {File}    file      - The binary file to convert
 * @param {string}  [category]        - A Category for the file, will be used as a subfolder to extract files. Can have subcategories like "category/subcategory". Default is the name of the folder containing the file.
 * @param {string}  [outputFilePath=Same folder, same name with .json extension]  - The file name for the output.
 * @param {string}	[varName=File name without extension]	- The name of the variable used to store the javascript object.
 * @return {File} The jsxinc file created
 */
DuBinary.toJsxincFile = function(file,category,outputFilePath,varName)
{
    category = def (category, file.parent.name );
    outputFilePath = def (outputFilePath, file.absoluteURI + '.jsxinc' );
	var duBinary = DuBinary.fromFile(file,category);
	return duBinary.toJsxincFile( outputFilePath, varName );
}

// ============== DEPRECATED ==================

/**
 * Creates a file from a DuBinary object representation.
 * @static
 * @deprecated
 * @param {DuBinary}	DuBinary  	- The DuBinary object containing the string representation.<br />
 * This object will be replaced by the File object created.<br />
 * If a file object is provided (the file has already been extracted), does nothing.
 * @param {string}			[outputFileName=DuFolder.duesfData/DuAEF/scriptName/category/binaryfilename]	- The output filename.
 * @param {boolean}			[onlyAtFirstRun=true]	- Does not extract the file if it already exists and this is not the first time this version of the script is being used.<br />
 * The file will be extracted only if the script is new or has just been updated (the version changed).
 * @return {File|null} The file created, null if the file could not be written.<br />
 * If the file is not written, check user permissions, and check if the file and network access preference is checked.
 * @example
 * //First, include the text representation of the file (Add a # before the include word)
 * include executable.exe.jsxinc
 * // Now, a variable called `executable` (the original file name without extension) is available, you can pass this object to the `DuBinary.toFile()` method to extract it and get an ExtendScript File object representation of it.
 * // Note: This object is an instance of a `DuBinary` class, which contains all information and a string representation of the original binary file
 * var execFile = DuBinary.toFile(executable);
 * //Now, the file exists in the file system, and execFile is an ExtendScript File object.
 * // The `DuBinary.toFile()` method extracts the file to the Application Data folder by default.
 * execFile.fsName; // C:\users\duduf\appData\Roaming\DuAEF\icon.png (Example on Windows)
 * @example
 * //You can specify the output file name. (Add a # before the include word)
 * include preset.ffx.jsxinc
 * var presetFile = DuBinary.toFile(preset,"C:/test/test_preset.ffx");
 * presetFile.fsName; // C:\test\test_preset.exe");
 * @category DuESF
 */
DuBinary.toFile = function(DuBinary,outputFileName,onlyAtFirstRun)
{
    onlyAtFirstRun = def( onlyAtFirstRun, true );
    if (!isdef( DuBinary )) return;
	if (DuBinary instanceof File) return;
    var rootFolder = DuFolder.duesfData.absoluteURI;
    if (DuESF.scriptName != '') rootFolder += '/'  + DuESF.scriptName;
	if (!isdef( outputFileName )) outputFileName = rootFolder + '/' + DuBinary.category + '/' + DuBinary.fileName;
	var file = new File(outputFileName);

  // performance
  // do not extract if the file exists and we're not in the first run (or debug mode)
  if (file.exists && !DuESF.scriptFirstRun && !DuESF.debug)
  {
    return file;
  }

	if (!file.exists)
	{
		var folder = new Folder(file.path);
		if (!folder.exists)
		{
			folder.create();
		}
	}

	file.encoding = 'BINARY';
	if (file.open('w'))
	{
		success = file.write(DuBinary.binAsString);
		file.close();
	}

	DuBinary = file;

	return file;
}

/**
 * Converts a JS file to a binary file
 * @deprecated
 * @static
 * @param {File}    jsFile        - The JS file to convert
 * @param {string}	[outputFileName=DuFolder.duesfData/category/binaryfilename]	- The output filename.
 * @return {File} The binary file created, or null if failed
 * @example
 * //If you don't want to include the file in the script. (Add a # before the include word)
 * var stringFile = new File("C:\test\image.jpg.jsxinc");
 * var jpgFile = DuBinary.convertToBinaryFile(stringFile);
 * // Warning, this method uses `$.eval()` which is a bad security issue.
 * // Do not use this method for anything else than debugging and testing.
 */
DuBinary.convertToBinaryFile = function(jsFile,outputFileName)
{
	//TODO parse instead of eval
	var obj = $.eval(jsFile);
	return this.toFile(obj);
}

// ==================== |---------| ====================
// ==================== | process | ====================
// ==================== |---------| ====================

/**
    * Constructs a new DuProcess instance
    * @class DuProcess
    * @classdesc A class to launch and manage processes, using the system command line.
    * @param {string}	  processPath     - The path to the process executable binary
    * @param {string[]}    [args]            - The common arguments used to run the process. You can append arguments each time you run the process with start(args)
    * @param {int}      [timeout=0]         - Waiting timeout after process start, in ms, 0 to avoid waiting, -1 for infinite.
    * If the process times out, process will not be killed, the script will just stop waiting.
    * @category DuESF
*/
function DuProcess( processPath, args, timeout ) {
    processPath = def( processPath, "");
    args = def( args, []);
    timeout = def( timeout, 0);

    /**
     * The path to the process executable binary
     * @type {string}
     * @name processPath
     * @memberof DuProcess
     * @readonly
     */
    this.processPath = processPath;

    /**
     * The common arguments used to run the process. You can append arguments each time you run the process with start(args)
     * @type {any[]}
     * @name args
     * @memberof DuProcess
     */
    this.args = args;

    /**
     * Waiting timeout after process start, in ms, 0 to avoid waiting, -1 for infinite.<br />
     * If the process times out, process will not be killed, the script will just stop waiting. Default: 0
     * @type {int}
     * @name timeout
     * @memberof DuProcess
     */
    this.timeout = timeout;

    /**
     * True if the process path leads to an application package (.app folder containing at least "/Contents/MacOS") on mac. False otherwise.
     * @type {Boolean}
     * @name isAppPackage
     * @memberof DuProcess
     * @readonly
     */
    this.isAppPackage = DuProcess.isAppPackage( processPath );

    /**
     * An Array of arguments array.<br />
     * Arrays of arguments in the queue will be processed one after each other.<br />
     * Update the Array and launch the queue with startQueue()
     * @type {string[][]}
     * @name queue
     * @memberof DuProcess
     */
    this.queue = [];

    /**
     * The latest command which has been run.
     * @type {string}
     * @name latestCommand
     * @memberof DuProcess
     * @readonly
     */
    this.latestCommand = '';
}

/**
 * Starts the process
 * @memberof DuProcess
 * @param {Array}  args  - Args to append to {@link DuProcess.args} before starting
 * @param {int}		[timeout] 	- Overrides the default timeout.
 */
DuProcess.prototype.start = function ( args, timeout ) {
    timeout = def(timeout, this.timeout);

    var cmd = this.buildCmd( args );

    this.startCmd( cmd );
    this.latestCommand = cmd;
    if (timeout > 100) this.waitForFinished( timeout );
}

/**
 * Starts the queue
 * @memberof DuProcess
 * @param {int}		[timeout] 	- Overrides the default timeout.
 */
DuProcess.prototype.startQueue = function ( timeout ) {
    if ( timeout === undefined ) timeout = this.timeout;

    var cmd = this.buildQueueCmd();

    this.startCmd( cmd );
    this.latestCommand = cmd;
    this.queue = [];

    this.waitForFinished( timeout );
}

/**
 * Waits for the process to finish
 * @memberof DuProcess
 * @param {int}		[timeout] 	- Overrides the default timeout.
 */
DuProcess.prototype.waitForFinished = function ( timeout ) {
    if ( timeout === undefined ) timeout = this.timeout;
    //wait
    var wait = true;
    var currentDate = new Date();
    var timeout = currentDate.getTime() + timeout;
    var checkTime = new Date();

    var checkCmd = '';
    var processName = new File( this.processPath ).name;
    if ( DuSystem.mac ) checkCmd = 'ps -A | grep "' + processName + '"';
    if ( DuSystem.win ) checkCmd = 'tasklist /FO TABLE /NH /FI "Imagename eq ' + processName + '"';

    //wait
    while ( wait ) {
        var test = system.callSystem( checkCmd );
        if ( test.indexOf( processName ) < 0 ) break;
        $.sleep( 100 );
        checkTime = new Date();

        if ( this.timeout >= 0 ) {
            wait = checkTime.getTime() < timeout;
        }
    }
}

/**
 * Builds and returns the command line
 * @memberof DuProcess
 * @param {string[]}  [args]  - Args to append to DuProcess.args before starting
 * @return {string}	The command
 */
DuProcess.prototype.buildCmd = function ( args ) {
    if ( args === undefined ) args = [];
    //build command
    //make sure to use fsName
    if ( this.processPath.indexOf( '/' ) >= 0 || this.processPath.indexOf( '\\' ) >= 0 ) {
        var processFile = new File( this.processPath );
        this.processPath = processFile.fsName;
        if ( this.processPath.indexOf( ' ' ) > 0 ) {
            this.processPath = '"' + this.processPath + '"';
        }
    }
    var cmd = "";
    // if it's an app package, prepend "open -n " and append " --args"
    if (this.isAppPackage)
    {
        cmd = "open -n " + this.processPath;
        if (args.length > 0 || this.args.length > 0)
        {
            cmd += " --args";
        }
    }
    else
    {
        cmd = this.processPath;
    }

    //args
    for ( var a = 0; a < this.args.length; a++ ) {
        var arg = this.args[ a ];
        if (jstype(arg) != 'string') arg = arg.toString();
        if ( arg.indexOf( ' ' ) > 0 ) {
            arg = '"' + arg + '"';
        }
        cmd += ' ' + arg;
    }
    for ( var b = 0; b < args.length; b++ ) {
        var arg = args[ b ];
        if (jstype(arg) != 'string') arg = arg.toString();
        if ( arg.indexOf( ' ' ) > 0 ) {
            arg = '"' + arg + '"';
        }
        cmd += ' ' + arg;
    }
    
    return cmd;
}

/**
 * Builds and returns the command line to launch the current queue
 * @memberof DuProcess
 * @return {string}	The command
 */
DuProcess.prototype.buildQueueCmd = function () {
    var cmd = '';

    if ( this.queue.length == 0 ) {
        return this.buildCmd();
    }

    for ( var i = 0; i < this.queue.length; i++ ) {
        if ( i > 0 ) {
            if ( DuSystem.mac ) cmd += ' && ';
            if ( DuSystem.win ) cmd += '\r\n';
        }
        cmd += this.buildCmd( this.queue[ i ] );
    }
    return cmd;
}

/**
 * Starts a command (in another thread).
 * @memberof DuProcess
 * @param {string}  cmd  - The command to start
 */
DuProcess.prototype.startCmd = function ( cmd ) {
    //Start
    if ( DuSystem.mac ) {
        //add ' &' at the end to start detached
        cmd += ' &';
        system.callSystem( cmd );
    }
    if ( DuSystem.win ) {
        //create batch file
        var batName = 'DuProcess_' + new Date().getTime() + '.bat'
        var bat = new File( DuFolder.duesfData.absoluteURI + '/' + batName );
        bat.encoding = "CP437";
        cmd += '\ndel "' + DuFolder.duesfData.fsName + '\\' + batName + '"';
        bat.open( 'w' );
        bat.write( cmd );
        bat.close();
        bat.execute();
    }
}


// ================ STATIC =====================

/**
    * Runs a command with some arguments.
    * @static
    * @param {string|File} process - The process or a path to the process.
    * @param {string[]} [args=[]] - The arguments to pass to the command.
    * @param {bool} [detached=false] - The script won't wait for the command to finish.
    * @return {string}	The output from the command.
    */
DuProcess.run = function( process, args, detached ) {
    detached = def(detached, false);
    var p;
    if (process instanceof File) p = new DuProcess(process.absoluteURI, args);
    else p = new DuProcess(process, args);

    var output = "";
    if (detached) p.start();
    else {
        var cmd = p.buildCmd();
        output = system.callSystem( cmd );
    }
    return output;
}

/**
 * Checks if a given path leads to an Application package on Mac, i.e. it's a path ending with <code>.app</code> containing at least <code>Contents/MacOS/</code>.
 * @static
 * @param {string|Folder|File} path - The path to the file or folder to check.
 * @return {bool} true if path is an application package.
 */
DuProcess.isAppPackage = function (path) {
    if (DuSystem.win) return false;

    if (path instanceof File || path instanceof Folder) path = path.absoluteURI;
    if (!DuString.endsWith( path, ".app")) return false;
    var testFolder = new Folder(path + "/Contents/MacOS/");
    return testFolder.exists;
}

/**
 * Constructs a queue of different processes
 * @class DuProcessQueue
 * @classdesc A class to launch several processes
 * @param {DuProcess[]}	  [processes]     - The DuProcess Array
 * @category DuESF
 */
function DuProcessQueue( processes ) {
    /**
     * The DuProcess list
      *@name processes
     * @type {DuProcess[]}
     * @memberof DuProcessQueue
     */
    this.processes = def (processes, []);
}

/**
 * Starts the processes
 * @memberof DuProcessQueue
 */
DuProcessQueue.prototype.start = function () {
    if ( !this.processes.length ) return;
    var cmd = '';
    for ( var i = 0; i < this.processes.length; i++ ) {
        var p = this.processes[ i ];
        if ( i > 0 ) {
            if ( DuSystem.mac ) cmd += ' && ';
            if ( DuSystem.win ) cmd += '\r\n';
        }
        cmd += p.buildQueueCmd();
        p.queue = [];
    }

    this.processes[ 0 ].startCmd( cmd );
}
// ==================== |-----| ====================
// ==================== | xml | ====================
// ==================== |-----| ====================

/**
* XML tools
* @namespace
* @category DuESF
*/
var DuXML = {};

/**
 * Parses the value of the XML object.<br />
 * If this object length is not 1, an Array is returned
 * @memberof DuXML
 * @param {XML} xml - The xml to parse
 * @param {string} [type='string'] - The type, one of 'string', 'bool', 'int', 'float' or 'date'.
 * @return {any[]|any} The value or an Array of values
 */
DuXML.getValue = function (xml,type)
{
	type = def(type, 'string');
	var value;

	if(xml.length() > 1)
	{
		value = [];
		for (var i = 0 ; i < xml.length() ; i++)
		{
			var val = DuXML.getValue(xml[i],type);
			value.push(val);
		}
	}
	else
	{
		var xmlString = xml.toString();
		if (type.toLowerCase() == 'float')
		{
			value = parseFloat(xmlString);
		}
		else if (type.toLowerCase() == 'int' || type.toLowerCase() == 'integer')
		{
			value = parseInt(xmlString);
		}
		else if (type.toLowerCase() == 'boolean' || type.toLowerCase() == 'bool')
		{
			value = DuString.parseBool(xmlString);
		}
		else if (type.toLowerCase() == 'date')
		{
			var reDate = /(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{1,2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d)/gim
			var matches = reDate.exec(xmlString);
			var year = parseInt(matches[4]);
			var month = DuDate.getMonth(matches[3]);
			var day = parseInt(matches[2]);
			var hours = parseInt(matches[5]);
			var min = parseInt(matches[6]);
			var sec = parseInt(matches[7]);
			value = new Date(year, month, day, hours, min, sec);
		}
		else
		{
			value = xmlString;
		}
	}

	return value;
}
// ==================== |-----| ====================
// ==================== | xmp | ====================
// ==================== |-----| ====================

/**
 * XMP tools
 * @namespace
 * @category DuESF
 */
var DuXMP = {};

DuXMP.initialized = false;

/**
 * Loads the XMP library if it has not been loaded yet. There is no need to call this function as it's called automatically by DuESF methods if needed.<br />
 * Call it once if you plan to use XMP without the methods in DuESF.<br />
 * Note that the XMP library is added statically as <code>ExternalObject.AdobeXMPScript</code>.
 */
DuXMP.init = function ()
{
    if (DuXMP.initialized) return;
    
    // load the library
    ExternalObject.AdobeXMPScript = def ( ExternalObject.AdobeXMPScript, new ExternalObject( "lib:AdobeXMPScript") );

    DuXMP.initialized = true;
}// ==================== |-----| ====================
// ==================== | zip | ====================
// ==================== |-----| ====================

/**
    * Zip methods. This lib needs the 7zip command line tool <code>7za.exe</code> to be located next to the script on Windows.
    * @namespace
    * @category DuESF
    */
var DuZip = {};

if ( DuSystem.win ) DuZip.processArgs = ['a', '-mx9'];
else DuZip.processArgs = ['-r', '-X'];

/**
 * The path to 7za.exe or 7z.exe (windows only, mac uses the internal command 'zip')
 * @memberof DuZip
 * @type {string}
 */
DuZip.processPath = '';

if ( DuSystem.win ) DuZip.processPath = DuESF.file.parent.absoluteURI + '/7za.exe';
else DuZip.processPath = 'zip';

/**
 * Compresses the content of a folder
 * @memberof DuZip
 * @method
 * @param {Folder} folder The folder to zip
 * @param {string} [archiveName=folder.name + ".zip"] The archive name
 * @param {boolean} [wipeFolder=false] Set to true to remove the original folder as soon as the zip is ready
 * @return {File} The zip file
 */
DuZip.compressFolderContent = function (folder, archiveName, wipeFolder) {
    wipeFolder = def (wipeFolder, true);
    archiveName = def(archiveName, DuPath.getName(folder) + ".zip");
    if (archiveName == '') archiveName = folder.name + ".zip";

    var folderPath = folder.fsName;
    if (DuSystem.win) folderPath += "\\*";

    var args = DuZip.processArgs.concat( [folder.parent.fsName + "/" + archiveName, folderPath ] );

    if ( DuSystem.win && !new File(DuZip.processPath).exists ) {
        alert("7zip not found. Cannot zip folder: " + folder.fsName);
        return null;
    }

    var processes = [ new DuProcess(DuZip.processPath, args) ];

    if (wipeFolder) {
        var wipeProcessPath = '';
        var wipeProcessArgs = [];
        if (DuSystem.win) {
            wipeProcessPath = 'rmdir';
            wipeProcessArgs = [ '/Q', '/S', folder.fsName];
        }
        else {
            wipeProcessPath = 'rmdir';
            //TODO test on mac. For safety, set to interactive mode. change the flags to '-rf' when tests are run succesfully
            wipeProcessArgs = [ '-ir', folder.fsName];
        }
        processes.push( new DuProcess( wipeProcessPath, wipeProcessArgs ) );
    }

    var processQueue = new DuProcessQueue(processes);
    processQueue.start();

    return new File( folder.parent.fsName + "/" + archiveName );
}// ==================== |-----| ====================
// ==================== | oca | ====================
// ==================== |-----| ====================

/**
 * @class
 * @name DuOCADocument
 * @classdesc An Open Cel Animation document<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuOCA.load} to create an OCA object.
 * @property {string} ocaVersion - The version of OCA used to export this document
 * @property {string} name - The name of this project
 * @property {int} width - The width, in pixels
 * @property {int} height - The height, in pixels
 * @property {int} frameCount - The duration, in frames
 * @property {float} frameRate - The frame rate, in frames per second
 * @property {float} pixelAspect - The pixel aspect ratio
 * @property {OCALayer[]} layers - The layers
 * @property {int} startTime - The frame number at which the animation starts
 * @property {int} endTime - The frame number at which the animation ends
 * @property {DuOCA.colorDepths} colorDepth - Bits per channel used in the document
 * @property {float[]} backgroundColor - The background color
 * @property {string} originApp - The application name from which the document was exported.
 * @property {string} originAppVersion - The version of the origin application.
 * @property {Folder} folder - The folder containing the oca files.
 * @property {string} path - The URI to the folder containing the oca files.
 * @category DuOCA
 */

/**
 * @class
 * @name DuOCALayer
 * @classdesc An OCA Layer<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuOCA.load} to create an OCA object containing the layers.
 * @property {string} name - The layer name
 * @property {OCAFrame[]} frames - The keyframes of the animation for this layer.
 * @property {OCALayer[]} childLayers - The child layers if this layer is a group.
 * @property {DuOCA.LayerType} type - The type of the layer. See the Layer Types section below
 * @property {string} fileType -  The type of the files used for the frames. The file extension, without the initial dot.
 * @property {DuOCA.BlendingModes} blendingMode - The blending mode of the layer
 * @property {bool} inheritAlpha - The inherit alpaha option (preserve transparency)
 * @property {bool} animated - Whether this layer is a single frame or not.
 * @property {int[]} position - The coordinates of the center of the layer, in pixels [X,Y] in the document coordinates. 
 * @property {int} width - The width, in pixels.
 * @property {int} height - The height, in pixels.
 * @property {int} label - A label for the layer.
 * @property {float} opacity - The opacity, in the range 0.0-1.0
 * @property {bool} visible - True if the layer is visible
 * @property {bool} reference - Whether the layer is a guide or reference, and should not be rendered.
 * @property {bool} passThrough - Whether the layer is in pass through mode. Only for grouplayer.
 * @category DuOCA
 */

 /**
 * @class
 * @name DuOCAFrame
 * @classdesc An OCA Frame<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuOCA.load} to create an OCA object containing the layers.
 * @property {string} name - The layer name
 * @property {string} fileName - The path and name of the file of the frame. It is the absolute path from the root of the OCA folder.
 * @property {int} frameNumber - The frame in the document at which the frames starts to be visible
 * @property {float} opacity - The opacity, in the range 0.0-1.0
 * @property {int[]} position - The coordinates of the center of the layer, in pixels [X,Y] in the document coordinates.
 * @property {int} width - The width, in pixels.
 * @property {int} height - The height, in pixels.
 * @property {int} duration - The duration of the frame, in frames.
 * @category DuOCA
 */

/**
 * Open Cel Animation interchange tools
 * @namespace
 * @category DuOCA
 */
var DuOCA = {};

/**
    * The different layer types
    * @readonly
    * @enum {string}
    */
DuOCA.LayerType = {
    PAINT: 'paintlayer',
    VECTOR: 'vectorlayer',
    GROUP: 'grouplayer'
}


/**
 * Loads an OCA document from a json file
 * @param {File|string} file - The .json file or its path
 * @return {DuOCADocument|null} The OCA document or null if the file could not be parsed or opened
 */
DuOCA.load = function ( file )
{
    if (!DuDebug.checkVar( file, 'file', undefined, 'DuOCA.load(file)') !== true) return null;

    if ( typeof file === 'string' ) file = new File( file );

    if (!DuDebug.checkVar( file, 'file', 'File', 'DuOCA.load(file)') !== true) return null;
    
    if ( !file.exists ) throw "DuOCA.load(file): file does not exist.";

    var data = DuFile.parseJSON( file );

    //add containing folder
	data.folder = file.parent;
	data.path = data.folder.absoluteURI;

	return data;
}

// ==================== |-----| ====================
// ==================== | url | ====================
// ==================== |-----| ====================


/**
 * Methods to manage URLS
 * @namespace 
 * @category DuESF
 */
var DuURL = {};

/**
 * The list of escaped characters in URLs
 */
DuURL.EscapedChars = {
    "%": "%25",
    " ": "%20",
    "#": "%23",
    "$": "%24",
    "&": "%26",
    "@": "%40",
    "`": "%60",
    "/": "%2F",
    ":": "%3A",
    ":": "%3B",
    "<": "%3C",
    "=": "%3D",
    ">": "%3E",
    "?": "%3F",
    "[": "%5B",
    "\\": "%5C",
    "]": "%5D",
    "^": "%5E",
    "{": "%7B",
    "|": "%7C",
    "}": "%7D",
    "~": "%7E",
    "\"": "%22",
    "‘": "%27",
    "+": "%2B",
    ",": "%2C"
};


/**
 * Escapes common characters from a string to be included in a GET request URL.
 * @param {string} str The string to escape.
 * @returns {string} The escaped string.
 */
DuURL.escape = function( str )
{
    for( ch in DuURL.EscapedChars )
    {
        str = DuString.replace(str, ch, DuURL.EscapedChars[ch] );
    }
    return str;
}

/**
 * Builds a query, made of "key=value" pairs.
 * @param {Object} arr An associative array with key/value pairs. Values will be escaped.
 * @returns {string} The query, with the leading ?.
 */
DuURL.buildRequest = function( arr )
{
    var q = '?';
    var first = true;
    for( key in arr )
    {
        if (!arr.hasOwnProperty(key)) continue;
        if (!first) q += '&';
        first = false;
        q += key;
        if (arr[key] != '') q += "=" + DuURL.escape( arr[key] );
    }
    return q;
}

/**
 * Builds a HTTP GET request. The user-agent is named after DuESF.scriptName.
 * @param {string} host The host, without port, without http part; for example: "duduf.com" or "version.rxlab.io"
 * @param {string[]} [subfolders] The subfolders.
 * @param {Object} [argsArray] An associative array with key/value pairs. Values will be escaped.
 * @param {string} [httpVersion="1.1"] An associative array with key/value pairs. Values will be escaped.
 * @returns {string} The query ready to be posted with a socket
 */
DuURL.buildGET = function( host, subfolders, argsArray, httpVersion )
{
    subfolders = def(subfolders, []);
    httpVersion = def(httpVersion, "1.1");

    var request = 'GET ';
    
    // add subfolders
    for (var i = 0, n = subfolders.length; i < n; i++)
    {
        request += '/' + subfolders[i];
    }
    request += '/';

    // query
    if (isdef( argsArray ))
    {
        request += DuURL.buildRequest( argsArray );
    }

    request += '  HTTP/' + httpVersion + '\n';

    // Build user agent
    var userAgent = 'User-Agent: ' +
        DuESF.scriptName +
        '/' + DuESF.scriptVersion.fullVersion +
        ' (' + DuString.trim($.os) + ') AE/' +
        DuESF.hostVersion.version;

    request += userAgent + '\nHost: ' + host + '\n';

    return request;
}// ==================== |------| ====================
// ==================== | core | ====================
// ==================== |------| ====================

/**
    * Methods related to ScriptUI
    * @namespace
    * @category DuScriptUI
    */
var DuScriptUI = {}

// ==================== |---------| ====================
// ==================== | strings | ====================
// ==================== |---------| ====================

/**
 * The list of strings used by the UI.
 * @enum {string}
 */
DuScriptUI.String = {
    ABOVE: "Above",
    ABOVE_SHORT: "Ab",
    ADD_ITEM_OR_CAT: "Add new item or category",
    AE_BLUE: "After Effects Blue",
    AE_BLUE_TIP: "The After Effects highlighting blue",
    AE_ORANGE: "After Effects Orange (CS6)",
    AE_ORANGE_TIP: "The After Effects highlighting orange from good ol'CS6",
    APPLY: "Apply",
    APPLY_ALL: "Apply all",
    APPLY_SETTINGS: "Apply changes to the settings.",
    APPLY_SETTINGS_ALERT: "You may need to restart the script for all changes to take effect.",
    ARM: "Arm",
    ARTHROPOD: "Arthropod",
    AUDIO: "Audio",
    AXIS: "Axis",
    BACK_PREVIOUS: "Back",
    BACK_LOCATION: "Back",
    BACK_LOCATION_SHORT: "Bk",
    BIRD: "Bird",
    BODY: "Body",
    BONE: "Bone",
    BUG_REPORT: "Bug report",
    BUG_REPORT_TIP: "Bug report\nFeature request\n\nTell us what's wrong or request a new feature.",
    CAM: "Cam",
    CAMERA: "Camera",
    CANCEL: "Cancel",
    CALF: "Calf",
    CATEGORY: "Category",
    CHARACTER: "Character",
    CHANNEL: "Channel",
    CHECK_UPDATE: "Check for updates",
    CIRCLE: "Circle",
    CLAWS: "Claws",
    CLEAR_LIB_TIP: "Clears the current search and category",
    CLOSE: "Close",
    COLOR: "Color",
    COLOR_HIGHLIGHT_TIP: "Set the highlight color.",
    COPY: "Copy",
    CREATE: "Create",
    CUSTOM: "Custom",
    CUSTOM_COLOR_TIP: "Select a custom color.",
    CLOSE_SETTINGS: "Close settings",
    DEFAULT: "Default",
    DEV_MODE: "Dev and Debug mode",
    DEV_MODE_TIP: "Use at your own risk!",
    DIGITIGRADE: "Digitigrade",
    EAR: "Ear",
    EDIT: "Edit",
    EDIT_ITEM_OR_CAT: "Edit selected item or category",
    EDIT_SETTINGS: "Edit settings",
    EFFECTS: "Effects",
    EXPERT: "Expert",
    EXPERT_UI_TIP: "The smallest UI, for expert users.",
    EYE: "Eye",
    EYES: "Eyes",
    EYEBROW: "Eyebrow",
    FEATHER: "Feather",
    FEATHERS: "Feathers",
    FEATURE_REQUEST: "Feature request",
    FEATURE_REQUEST_TIP: "Feature request\nRequest something new.",
    FILE: "File",
    FIN: "Fin",
    FINGERS: "Fingers",
    FISH: "Fish",
    FISHBONE: "Fishbone",
    FISHBONES: "Fishbones",
    FISH_SPINE: "Fish spine",
    FOOT: "Foot",
    FOREARM: "Forearm",
    FRAME: "Frame",
    FRAMES: "Frames",
    FRONT: "Front",
    FRONT_LEG: "Front leg",
    FRONT_SHORT: "Fr",
    FUNDING_BAR_TIP: "Thank you for your donations!",
    FUNDING_BAR_TIP_DETAILS: "This month, the {#} fund is ${#}.\nThat's {#}% of our monthly goal ( ${#} )\n\nClick on this button to join the development fund!",
    GROUPS: "Groups",
    HAIR: "Hair",
    HAND: "Hand",
    HEAD: "Head",
    HEEL: "Heel",
    HELP: "Help",
    HELP_TIP: "Get help.",
    HIPS: "Hips",
    HOMINOID: "Hominoid",
    HOOF: "Hoof",
    INVERT: "Invert",
    INVERTED: "Inverted",
    ISOLATE: "Isolate",
    ITEM: "Item",
    LANGUAGE_TIP: "Set the language of the interface.",
    LAYER: "Layer",
    LAYERS: "Layers",
    LEFT: "Left",
    LEFT_SHORT: "L",
    LEG: "Leg",
    LIMB: "Limb",
    LOCATION: "Location",
    LOCATION_SHORT: "Loc",
    MAGIC: "Magic is happening",
    MAIN: "Main",
    MASKS: "Masks",
    MAXIMUM: "Maximum",
    MIDDLE: "Middle",
    MIDDLE_SHORT: "Md",
    MINIMUM: "Minimum",
    MORE_OPTIONS_TIP: "[Shift]: More options...",
    MOUTH: "Mouth",
    NAME: "Name",
    NECK: "Neck",
    NEXT: "Next",
    NONE: "None",
    NORMAL_MODE: "Normal mode",
    NOSE: "Nose",
    NULL_OBJECT: "Null",
    OK: "OK",
    OPACITY: "Opacity",
    OPEN: "Open",
    OPEN_FOLDER: "Open folder",
    ORIGINAL: "Original",
    PATH: "Path",
    PAW: "Paw",
    PENIS: "Penis",
    PINCER: "Pincer",
    PIN_TIP: "Keeps this panel open",
    PLANTIGRADE: "Plantigrade",
    PREVIOUS: "Previous",
    POLYGON: "Polygon",
    POSITION: "Position",
    PROPERTIES: "Properties",
    RANDOM_TIP: "Set a random value.",
    RECTANGLE: "Rectangle",
    REFRESH_LIB: "Refresh library",
    REMOVE: "Remove",
    REMOVE_ITEM_OR_CAT: "Remove selected item or category",
    RESET_SETTINGS: "Reset the settings to their default values.",
    RIGHT: "Right",
    RIGHT_SHORT: "R",
    ROOKIE: "Rookie",
    ROOKIE_UI_TIP: "The easiest-to-use mode, but also the biggest UI.",
    ROTATION: "Rotation",
    ROUNDED_RECTANGLE: "Rounded rectangle",
    RUN_EXECUTE: "Run",
    RX_PURPLE: "RxLab Purple",
    RX_PURPLE_TIP: "The RxLaboratory Purple",
    RX_RED: "Rainbox Red",
    RX_RED_TIP: "The Rainbox Productions Red",
    SAVE_AS: "Save as",
    SCALE: "Scale",
    SCRIPTING: "Scripting",
    SELECT_LAYERS: "Select layers",
    SELECT_GROUPS: "Select groups",
    SET_QUALITY: "Set quality",
    SETTINGS: "Settings",
    SETTINGS_FILE: "Settings file",
    SETTINGS_FILE_TIP: "Set the location of the settings file.",
    SHOULDER: "Shoulder",
    SHOULDERS: "Shoulders",
    SHOULDERS_AND_NECK: "Shoulders & neck",
    SIDE: "Side",
    SIZE: "Size",
    SNAKE: "Snake",
    SPINE: "Spine",
    STANDARD: "Standard",
    STANDARD_UI_TIP: "The standard not-too-big UI.",
    START_TYPING_CMD: "Start typing something...",
    TAIL: "Tail",
    TAIL_SHORT: "Tl",
    TARGET: "Target",
    TEXT: "Text",
    TEXTURE: "Texture",
    THIGH: "Thigh",
    TIMELINE: "Timeline",
    TIP_BONE: "Tip",
    TRANSFORM: "Transform",
    TOES: "Toes",
    TORSO: "Torso",
    TYPE: "Type",
    UI_MODE_TIP: "Select the UI mode.",
    UNDER: "Under",
    UNDER_SHORT: "Un",
    UNGULATE: "Ungulate",
    VERTEBRAE: "Vertebrae",
    VULVA: "Vulva",
    WING: "Wing",
    WORKING: "Working...",
    X_POSITION: "X Position",
    Y_POSITION: "Y Position"
}

// OS Specific strings
if (DuSystem.win) {
    DuScriptUI.String.CTRL_KEY = "Ctrl";
    DuScriptUI.String.SHIFT_KEY = "Shift";
    DuScriptUI.String.ALT_KEY = "Alt";
}
else {
    DuScriptUI.String.CTRL_KEY = "Cmd";
    DuScriptUI.String.SHIFT_KEY = "Shift";
    DuScriptUI.String.ALT_KEY = "Alt";
}

DuScriptUI.String.CTRL_KEY_TIP = "[" + DuScriptUI.String.CTRL_KEY + "]";
DuScriptUI.String.SHIFT_KEY_TIP = "[" + DuScriptUI.String.SHIFT_KEY + "]";
DuScriptUI.String.ALT_KEY_TIP = "[" + DuScriptUI.String.ALT_KEY + "]";// ==================== |-------| ====================
// ==================== | icons | ====================
// ==================== |-------| ====================

// very small 18px
// ==================== |------------| ====================
// ==================== | w8_ae_blue | ====================
// ==================== |------------| ====================

var w8_ae_blue = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\b\x00\x00\x00\b\b\x06\x00\x00\x00\u00C4\x0F\u00BE\u008B\x00\x00\x00\tpHYs\x00\x00\x03\u00B0\x00\x00\x03\u00B0\x01'\u00C4\x0F\u00AD\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\x16IDAT\x18\u0095c\u00D4\u00EDy\u00FD\u009F\x01\x0F`\u00C2'9|\x14\x00\x00\u00B9|\x02\u00B3i>m(\x00\x00\x00\x00IEND\u00AEB`\u0082", "w8_ae_blue.png", "w8" );
w8_ae_blue;
// ==================== |--------------| ====================
// ==================== | w8_rx_purple | ====================
// ==================== |--------------| ====================

var w8_rx_purple = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\b\x00\x00\x00\b\b\x06\x00\x00\x00\u00C4\x0F\u00BE\u008B\x00\x00\x00\tpHYs\x00\x00\x03\u00B0\x00\x00\x03\u00B0\x01'\u00C4\x0F\u00AD\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\x16IDAT\x18\u0095c\\\u00AAv\u00E4?\x03\x1E\u00C0\u0084Or\u00F8(\x00\x00\u00A4\u009D\x02\u009E=\u008C7\u00F7\x00\x00\x00\x00IEND\u00AEB`\u0082", "w8_rx_purple.png", "w8" );
w8_rx_purple;
// ==================== |-----------| ====================
// ==================== | w8_rx_red | ====================
// ==================== |-----------| ====================

var w8_rx_red = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\b\x00\x00\x00\b\b\x06\x00\x00\x00\u00C4\x0F\u00BE\u008B\x00\x00\x00\tpHYs\x00\x00\x03\u00B0\x00\x00\x03\u00B0\x01'\u00C4\x0F\u00AD\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\x16IDAT\x18\u0095c|#!\u00F1\u009F\x01\x0F`\u00C2'9|\x14\x00\x00/\u00DE\x02+f\u00E7\x16^\x00\x00\x00\x00IEND\u00AEB`\u0082", "w8_rx_red.png", "w8" );
w8_rx_red;
// ==================== |--------------| ====================
// ==================== | w8_ae_orange | ====================
// ==================== |--------------| ====================

var w8_ae_orange = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\b\x00\x00\x00\b\b\x06\x00\x00\x00\u00C4\x0F\u00BE\u008B\x00\x00\x00\tpHYs\x00\x00\x03\u00B0\x00\x00\x03\u00B0\x01'\u00C4\x0F\u00AD\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\x16IDAT\x18\u0095c<>\u0089\u00E1?\x03\x1E\u00C0\u0084Or\u00F8(\x00\x00n?\x02h(Q\u00FBI\x00\x00\x00\x00IEND\u00AEB`\u0082", "w8ae_orange.png", "w8" );
w8_ae_orange;
// small 12px
// ==================== |---------------| ====================
// ==================== | w12_bugreport | ====================
// ==================== |---------------| ====================

var w12_bugreport = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x014IDAT(\u0091u\u008F\u00BF.\x04a\x14\u00C5\x7Fwf\u00D6D\u0096B\x14\u00A2C\u00B3\x14\x12\u00AF@#\u0091\u00EC\u00CEWH\u0088^\u00A2Rl/\"\u00E8E\u0094z2b\u00B3_au\u00E2\r\x04\r\u008DG\x10\u009D,3s\x1462&\u00D9\u00D3\u009D\u009C{\u00CF\x1F\u00A3\x02\u00EF}\x1Bh\x03\u00A1\u00A43\u00E7\u00DCqY\u00B72\u00E9v\u00BBkfv\u00F3\u00EF\u00C0l\u00A3\u00D5j\u00A5\x7F\u00BC\u00D3\u00E9LFQt\u009Ae\u00D9Q\x18\u0086\x0B\u00C0j\u00E5\u00E1\u00DE\u00CC\x1E$\u00EDeY\u00B6\x1B\x01HZ\x0E\u00C3p\u00C5\u00CCv\u008A\u00A2\u00B8\u00AA<\u00D4\u008B\u00A2\u00B8\u00FBW\u00C9{\u00DF\x00\x1E\u0081\u00B8\u00BAi\u0080~\x10\x04K\u00CDf\u00F3\u00E5o\u0083\u00F7\u00FE\x1D\u0098\x18\u00B8\u009EK\x12\u00B0=\u0090?\u0092$\u00F9\u00D5\u00BC\u00F7O\u00C0b\u00D9NR\u00DB\u00CC\n\u00E0\u00A4\u0092\u00F4\x1C\x01\u0087f6'i\x1F\x18\x1DR\u00E9\u00D3\u00CC\x0E$\u00BD\u0095+\u00BD\x00\u008D!\t\u00AFI\u0092\u00CC\x03\x04\x00i\u009A\u008E\x01_C\u00DC\u0091\u00F4\u00ED\u00BD\x1F\x07\b\u00D24\x1D\u0089\u00E3\u00F8\x16\u0098\u0097t\r\u00E4\u00A5\u00DB\\\u00D2\u00B5\u00995\u0080\u00DB^\u00AF\x17\x07\u00F5z}\x1A\u0098\u0095\u00B4\u00E5\u009C[\u00CF\u00F3|\u00AAV\u00AB]\x00\u0097y\u009EO9\u00E7\u00D6\u0081M`\u00A6\u00DF\u00EFO\u00FF\x00\x1F\u00ED}\u0096\u00BB}\u00F8+\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_bugreport.png", "w12" );
w12_bugreport;
// ==================== |-------------| ====================
// ==================== | w12_feature | ====================
// ==================== |-------------| ====================

var w12_feature = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00A3IDAT(\u0091\u009D\u00CF!\x0E\u00C2P\f\u00C6\u00F1\x7F\u00E1eO\u00A0`H\x12N\u00C1%0s\u00CFq\x02\x04\x1E\u0081\u00E7\x18$\u00A8\u00C9\x19n\u0081\u00E6\x0E\u00C8a&h1\u0088\u0097B\u00B6@U\u00D3\u00AF\u00BF\u00A4\x15\\5M\u00B31\u00B3\x13 \u00C0\u00A5\u00AA\u00AAu\u009E\u008F<P\u00D5\u00E9{\x19\x11)}\u00FE\x01\u00806\u00C3\u00AD\x0F{\u0081\u00EB\u00BF\x03\x11y\u00FC\x04\u00F2%\u0087\u00FF;)\u00F8A\u00D7u\u00B7\x18\u00E3\u00C1\u00CC&!\u0084\u00F3 \x00\n\u00A0\x04\x16fV\f\u0082\x18\u00E3\x1E\u00D8\u0089\b\u00AA\u00BA\x02\u0096C?\u00CC\u00B3~V\u00D7\u00F5\u00B8\x17\u00A8\u00EA\x11\u00B8\x02w3\u00DB\u00A6\u0094\u009Ey\u00FE\x02\u00AA34\x1FQ\u00E27e\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_feature.png", "w12" );
w12_feature;
// ==================== |----------| ====================
// ==================== | w12_help | ====================
// ==================== |----------| ====================

var w12_help = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00FDIDAT(\u0091\u008D\u00901J\x04A\x10E_\u00D5\u0088\u00A7\u00D0\u00CCx\x05\u00CD60\u00DAH\x19\u00BA\u00C1\u008B\u0098\u0098\x18\x18\u00AC\u00A8\u00A0\u0089\u0081\x18\u0089`b:\u00DDtf&\u0088\u00D9b\u00B0\x17P\u00CC\x14\x13\u00E3\u00A1\u00CB\u00C0\u0099e\x1CW\u00F4GU\u00FD\u00DF\u00A7\u00BAJ\u00E8\u00A8\u00AA\u00AAuU\u00DD\x07\u0086\u00C0\"\u00F0\u00A8\u00AAGeY\u00DE\u00B6\u008C\u00B4E\baSD\u00AA\x06\u00ECk\u00CF9w\f\u00A0\u00ED\u008B\u00AA\u008E\x1B\u00F8CD\x0EEd\f\u00BC7\u00F6A\ba\x19`\u00A1\r\x14E\u00B1]\u00D7\u00F5\u00A9\u0099\u009D9\u00E7\x1E\u009A\u00A9\u00AF\"r\x0E\x14\u00AA\u00BA\x06\u00BC\u00CC\u00BE\u00D4W\ba(\"\u00D7\u00C0\n\u0080\u0099\r\u00BC\u00F7S\u009D\x07\u00C7\x18OD\u00E4\u00BE\x03_x\u00EF\u00A7\u00DF\u0096\u00EE\u00C0\x1B\u00C0]\u00D3\u00BE\u0099\u00D9\u008E\u00F7\u00FEf\u00B6k?`fK\u009Dv\u00B7\x0B\u00FF\u00AA\u0094\u00D2j\u008Cq0\u00CF\u00FB1!\u00A5\u00E4s\u00CE\x13`\x12c\u00DC\u00FA3\u0090s\x1E\x01\x05_'\x1F\u00FD'pif\u00CF\u00C0\u0093\u0099]\u00F5\u00FDO3\u0095[g\u00AAB\u00A3I\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_help.png", "w12" );
w12_help;
// ==================== |--------------| ====================
// ==================== | w12_settings | ====================
// ==================== |--------------| ====================

var w12_settings = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x03IDAT(\u0091}\u00D0\u00A1N\u00C4@\x10\x06\u00E0\u00FFo\u00AFl5\u0086\u00A7@\u00B0\x1E\u0085C5}\x03Np\u0084\u0080E\u00AF\u00C6@\u00E0\x02\u0082#\x01[\u00D5\u00B4\u00C1\u009C\u00E4\x01:\u00EB\u00F0\b\x14\u00C1P\x10\\\u00E8`z\u00E4\u00B8k\x19\u00B7;\u00DF\u00CC\u009F\f\u00D1Sy\u009Eo\u0093<\x05\u00B0\u00A5\u00AA'I\u0092\\\x00\u00C0\u00A0\x07o\u0092\u009C\x02\u0088\x01\u00DC{\u00EF\u00C7\u00F3^\u00E7\x00\u00C9\u00A3\x16\u00BF\u0088\u00C8\u00D09\u00D7dY\x16\u00C6q|\u00CC\u00AE\u0081\u00A2(\x1ETu\x17\u00C0w\x14E\u00EBu]\x7F\x18c\u00EE\x00$+\t\u00CE\u00B9@U\u00D7\u00DAg8\u009B\u00CD\u00DE\u008C1\u00AF\x006\x00<r\x19[k'\u00AA\u00BA\u00D7\x11\u00FCIr\u0087\u00FFaU\u009D\u0092\u00FCR\u00D5\u00E7\u00A6i\u00C6i\u009A>\r\u00DA\x06\u00CB\u00B2\u00BCZ\u00DA|\u00EB\u00BD\u00DFw\u00CE5\x7F\x0E\u00D2\u00E2kU\x1D-b\x11Y\u00C1\x00\x10Zk/\x01\x1C.\u00FC\u00DD\u0088\u00C8\u00A8\x0B\x03@\x00`\u00F8\x1BGND\u00E4\u00A0\x0F\x03@\u00A0\u00AA\u00E7\x00\u00DEI\u009EUU\u00D5\u00BBy^?WT\x7F\u00BAa\u009Ae\u0093\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_settings.png", "w12" );
w12_settings;
// ==================== |----------| ====================
// ==================== | w12_back | ====================
// ==================== |----------| ====================

var w12_back = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u008EIDAT(\u0091\u008D\u00CF\u00B1\x11\u00C20\f\u0085\u00E1\x1F\u00D5\u00CC@K\u00C1\x0E)\u00E8\u008D\\\u00B2\x1D3\u00C8w9\u008A\f\u00C0\x00t\u00D4\u00EC@'Q\u00A5\b\u00B1\x1DTJ\u00EF\u00BB{\u00DA\u00D1\u0099R\u00CA9\"n\"rM)=\x00\u00A4\x156\u00B3!\"\f8D\u00C4q\u00DEW\u0081\u0099\r\u00C0\b\u00EC\x7Fo+\u00D0\x0B\u00AF\u00C0Vx\x01J)'\u00E0\u00DE\x0B/\u0080\u00BBK\u00ADb\x13\u00E4\u009C\u009F\u00C0\x05\u00F8\u00FC\x05\x00Tu\x02\u00B4\u0087V\x15\u00B6P\u00B5s\x0F5\u009FT\u00D5ID2\u00F0v\u00F7\u00D7\u00BC\u00FF\x02}\u009C3\x19\u00CA?SM\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_back.png", "w12" );
w12_back;
// ==================== |-----------| ====================
// ==================== | w12_close | ====================
// ==================== |-----------| ====================

var w12_close = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x1FIDAT(\u0091m\u0090\u00BDJ\x03Q\x10F\u00CFL,\u008C(\u0088\u00CF\u00E0\x13h)h!b\x11w\u00EF\u00DE\u00C6F\x10R\u00E9KI*\u00EBp\u00B3\x06\u00C5\b\x11\u00B4\x12|\u0082\u00D8Z\u008B\u009A\x1F\"\u00EC\x1D\u009BMX\u00C2~\u00E5\u0099o\x0E\u00CC\b@\b\u00E1\x04\u00E8\x00m\u00E7\u00DC\u0080Jz\u00BD\u00DE\u0091\u0099\u00DD\u008A\u00C8u\u009A\u00A6wR\u0096\x03\u00D0\x04ff\u0096fY\u00F6T\u008A\x0E\u0080{`\x0B\u00F8\x13\u0091s\t!\u008C\u0080\u00DD\u008At*\"\u0089\u0099\u00CD\u0081\x07`\u00B32\u00FBTU\u00BD\x04~*p\u00C3\u00CC\u00F2\u00D2\\-OD\u00E4B\x00\u00BA\u00DD\u00EE~\u00A3\u00D1x4\u00B3\x1D\u00EA3\x01\x12\u00E7\u00DCP\x16$\u00CF\u00F3=3\x1B\u00D4,Mb\u008Cg\u00DE\u00FBg\x00]\u00D0\u00A2(\u00D6\u00CDl\u00AD\u00C6.\"\u00B2\u00E4R\u00F3\u008D\u00BA,\u00BF'!\u0084C\u00A0\u00BFr\u00E0/P\x00\u00DB\u00D5%\u00C0)p\u00B3R\x1E\u009BY+\u00C6x\n|Wx\x13\u00E8\u00A8\u00AA\u00B6\u0081q\t\u00A7@\u009Ae\u00D9\u008B\u00F7\u00FE-\u00C6x\f|\u0095\u00B39p\u00A5I\u0092\u00BC\u00C6\x18\x13\u00E0#\u00C6\u00D8r\u00CE\r\x17J\u00EF\u00FD\u00BB\u00AA\u00B6\u0080\u0091\u00AA\u00A6\u00CE\u00B9\u00FE?q\u00EB}\u00EF\u00EA$\u00F9\u00F7\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_close.png", "w12" );
w12_close;
// ==================== |---------| ====================
// ==================== | w12_pin | ====================
// ==================== |---------| ====================

var w12_pin = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x13IDAT(\u0091\u0085\u008F\u00B1J\u00C3p\x18\u00C4\u00EF\u00FE\t\u0084\u00D2\u0087pt\x17\u00E9C\u0088$iT\u00DC\x1C\u0082\x0E...\u00F5\r\u009C\x15\u009D\\\x14AZ\u0084|)\x18\b\"]\u00DC\x1C\u00FA\x04\u00BE\u0080\u008E.M\u009A\u00E4s\n\u00A66\u00D6\u009B>\u00EE\u00FB\x1D\u00DC\x11+\x14\u00C7\u00F1\u00BA\u00AA\u00A6\u00C6\u00983\u00D7u\x1F\x00\u00C0\u00FC\x05O&\x13\u009B\u00E4\x1D\u00C95U\u00BD\x17\u0091C\x00\u00E0o0M\u00D3n\u0096e\u009B\u00AA\x1A\x028h\u00BC\u0094dh7a\x11\u00B9\u0098\u00CDf\u00C7\x00\x16\u00FCZUUu\x17*Y\u00965\x00\u00F0\u00D2\u00C2~\u0091\u00DC\u00F3}\u00FFj\u00A9R\u0092$\u00CE|>\x1F\x02\u00F0\x1A\u00F6\u00B6\u00E7yO@\u00CB\u00E8<\u00CF\u00B7H\u009E\x03\u00B8\u00AD\u00BD\u00B2,\u00DF\u00EA{! \"\u00BB$\u0087\x006\u00A6\u00D3iH\u00F2\x06\u00C0g\x10\x04\x1FK%\u00C7\u00E3\u00F1N\x1C\u00C7\u00B9\u0088\u009C\u00D4\u009E\u00AARD\u00F6\u009B\x1C\x01 \u008A\u00A2\u009E1\u00E6\u0095\u00E4\u00A9\u00EB\u00BA\u0097-\u00A3\x7F\x02\u00A3\u00D1\u00C8r\x1C\u00E7YU\u00AF}\u00DF\x7F\\\x05\x03\u0080\u00DD\u00E9tzEQ\x1C\u00F5\u00FB\u00FD\u00F7\u00FF`\x00\u00F8\x06Nlb'\u00CF\u00F9\u00A5W\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_pin.png", "w12" );
w12_pin;
// ==================== |------------| ====================
// ==================== | w12_pinned | ====================
// ==================== |------------| ====================

var w12_pinned = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00ADIDAT(\u0091\u008D\u0091\u00BB\r\u00C20\x10\u0086\u00BF\u00B3\u008C\x14\tQd$:\u00AA\u00B8d\x04\u00F6\u00A1\u008A\x18\u0080\u0082\n\u009B\x19\x18\x02\u0089\x1D(\u00A0r\u0084\u008F&AN\x14K\u00F9\u00AB\u00D3\u00FF\u00B8\u0087N\u00C8\u00E0\u00BD\u00DF\x017\u00C0\u00F6T\x07l\u009Ds\u00F7\u00C1c\u00F2\u0080\u0088Hf\x06X\u00A9\u00AA\u00E6\u009EQ\u00C0\x18\u00F3`\u0082\u0094\u00D2\u00B3\x18\u00881~\u0080\u00BCc\u00AA\u00AA\u00AA<\u00C1Z{\x04$\u00D7c\u008C\u00A7\u00D1\u00DAC\x11Bp\u00AAz\u009D\u00AE\x04 \"\u00FB\u00A6i.0>\u00B0\x16\u0091\u00C3\\\x00\u00F8\x16\u00F8\u00FF\u00B46\u0084\u00D0\u00CEiv\u008ET\u00D5u\u00A9\u0099)\t%H\u00FF\u00DD\u00CDB\u00FF\u00DB\x02g\u00A0^\x18x\u00FD\x00x\u00860\u00D4\r`O\u00AE\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_pinned.png", "w12" );
w12_pinned;
// ==================== |-------------| ====================
// ==================== | w12_options | ====================
// ==================== |-------------| ====================

var w12_options = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01$IDAT(\u0091\u0095\u00901J\x03a\x10\u0085\u00BFI~V#\u00EC\x11\u00B2v\u0082\u00C6\x03X(\b\x166j\u00B7\u00D1raC\u00BA\x14+\x1E`\x11\u00BCBX\u00C8O\u0090m\u00B7\x12\u00B5\u0091\u00E0\rl\u008D\u00E0\r\x04\u008B\x10\x02\x1A\u0092\u008C\u00CD&dc\u0095W\r\u00F3\u0086\u0099o\x1E\u00AC)Ym\x04A\u00B0i\u008C\u00B9\x06\u00CA\u00E3\u00F1\u00B8\u009D\u00A6\u00E9\u00F7\u00B2_\u009A\x17\u00ADVk\u00C3\u00F7\u00FD\u00B21\u00A6\x06\u00DC\x01\u00B7\u008E\u00E3\x1C\x02\u00D2l6\u00B7\u00FE]\b\u00C3\u00F0ADv\u0080m\u00A0\u0092\u00B7'\u00C0\x07\u00F03\x18\f\x0E\u00B2,\u009B\x1A\u0080F\u00A3\u00B1\x0B\\,\u00B6\u0088<\u00AB\u00EA\b\u00F0\u0081}\x00\u00D7uO\u0080\u0097\x12\u0080\u00B5\u00B6\u009Fc\x00\u00F4:\u009D\u00CE\u0099\u00B5\u00F6\x12H\x01T\u00F5\u00DE\u00F3\u00BC^\u00E1\x07\x11\u0099\u00AE\x06\u00A0\u00AA\u009A{\x1A\u00C7\u00B1.~\u00C8\u0091\u00DE\u0097f\x1FUu$\"\u00F5\u00F9\u00D2\u00D9lv\u00DA\u00EDv\x0BHO\u00AA\u00FA\t\u00FC\x02\u00E7\"r\x05(\u00D0\x07\u00DE<\u00CF{- \u00B9\u00AE[\u00AFV\u00AB{\"r\u00B4t\u00A9n\u00AD\u00AD9\u008Es\x1C\u00C7\u00F1\u00A4\x10\u00EB\\Q\x14U\u0086\u00C3\u00E1\rP6\u00C6\u00B4\u0093$\u00F9Z\u009DYK\x7F\x10\u00FB^\x18\u0080\u00E9g\u009B\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_options.png", "w12" );
w12_options;
// ==================== |-----------| ====================
// ==================== | w12_check | ====================
// ==================== |-----------| ====================

var w12_check = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00BFIDAT(\u0091\u0095\u0090\u00C1I\u00C4P\x14E\u00CF\u009D7=\x04+\x18lA{\u00C8\u00C7 n\x06\x14\x04;\x18,\u00C2\x16\x04w\u00A2\u00FCExI\x03)c\n\u00B0\u008D|\u009E\u009B0d\x06\x1D\u00E3]\u009F\u00F3\u00B8\u00F7\u00C1?\u00A3%P\u00DB\u00B6\u0097f\u00F6\x1A\x11\u00CF\x7F\n\x13<\x00\x15\u00F0uV\u00E8\u00BAn\x03\f\u00C0\x05P\"\u00E2~\u00ED\u00EEW\u0092^J)OM\u00D3\u00EC\u00E7\u0097'\u00B8\x02FI\u00DB\u0094R^I\u00FA\x04\u00AE\u00CDl\u0098\u00A0\u00D3\x1A\u00A3\u00A4m]\u00D7\x19`%i\x07\u008C@ef\u0083\u00BB\u00DF\u00FC\x06\x1F\u00BE\u00E4\u00EE\u00B7\u0092>\u0080\u00F5lB\x01\x1ERJ\u00EF\u00F3]\u0087\u00D1'\u00D2\u008F\u00F0\u0091\x00\u00D0\u00F7\u00FD]D\u00BCIz\u009C\u00D78\u009B\u009C\u00B3-\x02\u0097\u00E6\x1B\u0085\u0087H\x14\u00BC\u0092~\u00E6\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_check.png", "w12" );
w12_check;
// ==================== |-----------| ====================
// ==================== | w12_reset | ====================
// ==================== |-----------| ====================

var w12_reset = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01'IDAT(\u0091\u008D\u008F\u00BFK\x02a\x1C\u00C6?\u00EF\u00F7\u00DE\u00C3\u00A6\x02\x17q8j/\u0088\u00A0\u00BD$\u00A8\x10IIp\u00A9\u00A1\u00C9\u00EB\x0F\u00E8\u00AF\u00A9\u00B5\u00C9I=r=\u00B21P\x1A\x1CZj\u0089\u009B\u009A\\\u00C2|\u00BBoCG\u0098P\u00F6\u00D9\x1Ex~\u00F0\u0098(\u008A\u00F6\u0092$\u00E9\x07A \u00CE\u00B9\u00A6\u00AA\u009E\x00\x1B\x00\u00C6\u0098\x11pm\u00AD\u00BD,\u0097\u00CB\x13\x00\u00D3\u00E9t\x1Cp\x03\x04\u00C0\x16_|\x00)\u00E0g\u00FA\u00C19W\u00A9\u00D7\u00EB/\x02\bp\u0094\u0099G\"r \"\u00CB\u00C3\u00E1p\u00C9\x18\u00B3\x0B\u00DC\x03\u009B\u00D6\u00DA\u00A8\u00D7\u00EB\u00E5,?yL\u0092$\x0E\u00C3p\u009A\u00E9\u00DBV\u00AB\u00B5\u0093\u00CB\u00E5\u00FA\u00C0\u00B6s\u00AE)\u00C0\u00EBL\u00E0\u00B8P(\x1C\u00CE64\x1A\u008D7c\u00CCE&O\u00C5\u00F7\u00FD\u00C0\u00F3\u00BC\u00BC\u00E7yy\u00DF\u00F7Wj\u00B5Z4\u00B7\u00CA`0\u00B8\x03\u00DEUu\u00DDf\u00EF'\u00F3\u00A6Y\u008A\u00C5\u00A2\x07\x18@\u00E6?\u00FC\u008A\u0088T\u00D24\u00DD7\x00\u00DDn\u00F7JU\u00D7\x16d\u009E\u00AA\u00D5jh\u00E38\u00B6\u00E3\u00F1\u00F8\fX\u00B46U\u00D5s[*\u0095\\\u00BB\u00DD\u00AE\u0088\u00C8\u00EA_nU}6\u00C6\u00E8\x7F/|\u00F3\t'\u00ADc\x01\u008D\u00A7\u00A0@\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_reset.png", "w12" );
w12_reset;
// ==================== |------------| ====================
// ==================== | w12_update | ====================
// ==================== |------------| ====================

var w12_update = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01%IDAT(\u0091\u0095\u0091\u00B1J\x03q\f\u0087\u00BF\u00C4\u0083StP\u00D0gp\u00AE\u008B\u00F8\x02.r-t8p\x16\u009F@\u00C1\"\u008AU'\x17Wuu\x11\u00EAP\u008E\x1B\u00C5]\u0097>\u0082\x0F\u00A0\u008B\u00A2\x16\u00AE\\\u00FEq\u00B9+\u00A5\u00BA\u00F8\u009B\u0092\x1F\u00F9\u0092\u0090\u00C0?%\u00D3F\u009E\u00E7\u00CB!\u0084\x060gfO\u00EDv\u00FB\u00F5O\u00A0\u00DB\u00EDj\u00A3\u00D18\x05\u00F6\u0081\u00D9\u00CA\x1E\u0089\u00C8\u00A5\u00BB\u00AF\u00A8\u00EA]\u0092$\u008FQ\rT\u00C5\u0087\u00C0\u0099\u0088\u00DC\u00BA\u00FB\f\u00B0\u00EB\u00EE\x1D\x003{\x00\u0088\x00\u00FA\u00FD\u00FEb\u00D5\u00F9\u00BC\u00D5j\u009D\u00D6M\u00B2,[\u00AAcU\u00F51\x10E\u00D1|\b\u00E1\u00A4(\u008A\u009B\u00C9}U\u00F5\u00DA\u00CCzU\u00FA\f\u00A0\x00eY~\u00BB\u00FBK\x1C\u00C7\u009By\u009Eo\u00D4@\u0092$\u0083\u00D1h4\x10\u0091\u00B5\u00B2,\x17\u00C6\u0080\u00AAn\u0089\u00C8=\u00D03\u00B3\u00ED\u00C9)q\x1Cw\u0080c3\u00FB\x1C\u00AF4u\u00DEa\u0096e\u00AB\u00AA\x1A\u00CCl\x07\u00D8w\u00F7\u00C34M\u00BF\u00C6\u0080\u00BB\u00BF\u0089\u00C8\x15\u00F0.\"{\u00C0A\b\x01\x11\x19\u008A\u00C8Q\u00B3\u00D9\u00BC\u00F8\u00F5\u0087Z\u00D5\u00E3\u00D6\u0081aQ\x14\u00834M?\u00A6k\u00FE\u00A5\x1FC\u00C1x\u00DC\u00FA\u0087\r\u0095\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_update.png", "w12" );
w12_update;
// ==================== |-----------------| ====================
// ==================== | w12_box_checked | ====================
// ==================== |-----------------| ====================

var w12_box_checked = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00C7IDAT(\u0091\u008D\u00D0=J\u00C4`\x10\u00C6\u00F1\u00FF\u00F3\"\u0093\u00C2\u00EC\u00A6[O`#X\u00D8Z\u00D8\t\u00DB[\u00E4\x00\u00B9\u0084\u00E7\u00F0\"j\u00E7G\u00AD7\u00B0\x10\u00C1\x03\u00D8\u00C94I0\x19\u00AB\u0095\x10^\u00DC<\u00F5o\u0098yFM\u00D3\u00BCI:aA$}\u00A4\u00A5\x18 \"\u008E\u00D3R\u00BC\u00CB\u00DE\x01Iw\u00C0\x15\u00D0\x01\x1C\u00EC\u00F1\u00B7\u0092\u00EA\u00AE\u00EB\u00D6f\u00F6\rlv\x1BB\u00D25\u00F0:\u00C5)\u00A5\u00BAm\u00DB\u00B5\u0099=\x03\u009B\u00E9I_eY\u00DE\u008C\u00E3\u00B8\x05^2\u00F8l\u00DE\u00E1\u00C8\u00DD\u00EF\u00AB\u00AA\u00FA1\u00B3mJ\u00A9\x1E\u0086a5\u00C7\u00F3\u00D2\u0097\u00EE\u00FE\u00E0\u00EE\u00D1\u00F7\u00FD\u00A1\u00A4\u00C79\u00CE\u0095\u00BE(\u008A\u00E2\tX\x01\u00A7\u00B9/\u00E4\u00BEt\u009E\u0083\x7F'E\u00C4\u00FB\x7F`\u009A\u0088\u00F8\u00FC\x05\u0080\u00F3=\u00A5\u0097\u009B\u00CD\u00FD\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_box_checked.png", "w12" );
w12_box_checked;
// ==================== |-------------------| ====================
// ==================== | w12_box_unchecked | ====================
// ==================== |-------------------| ====================

var w12_box_unchecked = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00lIDAT(\u0091\u00ED\u0091\u00B1\r\u00C2@\x10\x04g\u00AD\u00EF\u00C7\u00D4CBr\u00DF\u0087\u0089)\u00E1%RK\u00D0\x0291\u00CD8\u00BCuB\u0086tr\x01L<#\u00AD\u00B4\u008A\u0088;p\x01&j\x12x6\u00E0\f,\u0092\u00DE\u00A5\u009Dy\u0092tk\u00C0$\u00E93\u00C6xUAD$\x07f\u00FC\u00F0\x0F\u008E\u00D0\u0080\u00B4=\u00F7\u00DE\u00B7J\u00FC\x1EG\u00B3\u00BDJ\u00BA\u00DAV\x15H2\u00F0\u00D8\x01\u0082\x05!+\x1F\u00A2 ^\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_box_unchecked.png", "w12" );
w12_box_unchecked;
// ==================== |------------| ====================
// ==================== | w12_folder | ====================
// ==================== |------------| ====================

var w12_folder = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00C7IDAT(\u0091\u00AD\u008E=j\x02a\x14E\u00CF{3\u0089\u00B8\u0094@z\u00DBt\u0081i\u0086Y\u0080k\u00B0s\x05)\u0092\u00DA=\x04\u00AC\u0086\u00D9AH\x11\x10\\@H\u00A7 \u00D66\u00C2\b\u00EF\u00BB)t\u0082\b\u0093\x1F\u00C8i\x1E\\\u00CE}\\\u00F8#V\u00D7\u00F5]\u0096e\u00A3.\u0088\u0088EUU/\u00BD\u0085\u00A6i\u00F6\u00C0\u00F0,;\u0098\u00D9\u00D8\u00DD\u0097]\u00E0\u00EE\u009B\u00A2(Z\u0080\u00FCB\x06\u00B8\u00964\u008F\u0088\u00AF \"\u00DE\u0081\u009B\u00AE\u00D0\u0091\u0080\u00F5\u00E9^ru>I\u00C0\u009B\u0099=H\u00BA53\u00EF\u00DB\u009FR\u00DA\u00E6\u00C0\u00FE$\u00D7\u00C0@R\u009F\u008F\u0099-\x1Cx\u00944\x01\x06\u00BD\u00E6\x11\x01S\u0097\u00F4\x01\u00DC\u00FF \x03<\u0097e\u00F9\u009A\u00BB\u00FB*\u00A5\u00F4\u00F4\u009Dif\u00BB\u00B6mg\u00BFx\u00FA\x0F|\x026hF\u00C5\u00D3$\u00AB\u00BF\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_folder.png", "w12" );
w12_folder;
// ==================== |----------| ====================
// ==================== | w12_file | ====================
// ==================== |----------| ====================

var w12_file = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u0085IDAT(\u0091cd```\u00D8\u00B0aC5##c\x0B\x03*\u00D8\u00C2\u00C0\u00C0p\u0081\u0091\u0091\u00F1\u009A\u009F\u009F\u00DFr\u0098 \x13\u0094\x16g\u00C0\x02\u00FE\u00FF\u00FF\u00CF\u00F1\u00FF\u00FF\u00FF\u00C5\x1B7n\u008CA\u00D7\u0080\x0F0300,\u00D8\u00B4i\u0093\x13!\rj\u00CC\u00CC\u00CC\u00EB\u00FE\u00FD\u00FBg\u00F2\u00EF\u00DF?sff\u00E6k\x045\u00FC\u00FB\u00F7\u00EF\x18\x13\x13\u00D3\x19&&\u00A63\u00BF\x7F\u00FFN \u00D6I(`\u00E4j`dd\u00FCHH!##\u00E3\x07\x06\x06\x06\x06\x000a&\x1B\u00F7\x01\x10\u00DE\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_file.png", "w12" );
w12_file;
// ==================== |----------| ====================
// ==================== | w12_next | ====================
// ==================== |----------| ====================

var w12_next = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u0084IDAT(\u0091\u008D\u00CD\u00B1\r\u00C20\x10F\u00E1w'*\u0086\u00A0\u00CD\x18(\x0387\x01\u00CB\u00B1\u00C1\x19\u00A4T4,\u0081\u00E8\u0099!\u009De\x1A\u0082\"\u0094\x0B\u00FE\u00AB\u0093\u00F5>Y\u00F8,\u00E7|\u00AC\u00B5\u009E\u0081\u0093\u0099\u00DD\t\u00A6\u008B\u00BB\x03\x0E\u00C0\u00E8\u00EE}\x0B\u0098\u00B7\x07\u00AE\x11Z\x03\u009B(\x02!\u00DA\x023\u00BA\u00B8{\u00D7\n\x00\u00A4\u0094\u00F2\u00EDv\x7F\u00E2IU\x073{\u00B4\u00FC0\u00A9\u00EA\u0090R\u00BA-\x1F#\u00B0\x1AG \u008C\x7F\u00C1\x13x\u0089H\u008Ab\u00807\x0E\u00C1*\x1A<\x1B\u00A2l\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_next.png", "w12" );
w12_next;
// ==================== |----------| ====================
// ==================== | w12_goto | ====================
// ==================== |----------| ====================

var w12_goto = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00FBIDAT(\u0091\u0085\u00D0\u00BDJ\u00C4@\x10\u00C0\u00F1\u00FF,\t\"\u00EA\u00B5\u00BE\u0089\"\u0082 6\u00DEB\u00B2y\x0F{\x1B\x11\u00E1\u00B0\u00D1\u00A7\u00F0\x116'\x06Si\u00E3\u0081\x0F\u00E0;\u0098\u00F6\x0EdI\u008A\u00B1\u00D9H\u00A2\u00C79\u00E5\u00CEov>\u0084\rQ\u0096\u00E5\x1E\u00D0\x00\u00DB\u00F1\u00E9S\u00CA\u00B2<\x15\u0091\u0083!T\u00D5\x07\u00E7\\\x13\u008B.\u0081\u00BB>\u0095\x00^U'c\u00AF\u00AF@SU\u00D5\u00A4m\u00DBBD~\u0092\t\u00B0+\"\u00B3<\u00CFo\u0086]\"~\x16\u0091#\u00E0\r\u0098\x03\u00D7f\u00DD\u00ECk\u00F0\u00D49wo\u008C\u00D9\u00FFSP\u00D7\u00F5N\u00D7u\u008F\x11/\"^\x01dY\u00F6\u0095\u00FC\u00C6!\u0084\n8\x01\x16i\u009AN\u00AD\u00B5\u00AB\u00A1\x19u\b!<\u00F5\x188\u00B7\u00D6.\x01\u00BC\u00F7W\u00DE\u00FB\u00C3~\u00E9\u00E1y>\u008C1\u00AA\u00AAy?\x06\u0080\u0088\u00DC\x02)\u00F0>*(\u008A\u00E2b\u00DD\x11\u0086\u0091\u00C4\u009F\u00CF\u00BC\u00F7[\u00FFX\x05HTu.\"ND\u008E7\u00E0%\u00F0\x02\u00F0\rs\u0080j\x1B\u00F2\t\u0097\u00B9\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_goto.png", "w12" );
w12_goto;
// ==================== |-----------------| ====================
// ==================== | w12_eye_dropper | ====================
// ==================== |-----------------| ====================

var w12_eye_dropper = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00F2IDAT(\u0091\u008D\u0090\u00ADN\u00C4P\x10\u0085\u00BFsoy\x03\u0092\u00CA\u00D5\x18\x1Cj\u00DF\u0080\u00A4\u00A1\x04r\u00E5&<\x03\u008A\x00\x02\x12\x16\x03XR\u0087\x02\u00D7\u00E4\u00D6\u00E01($\u008A\u0084\x04\x0B\t\u00BA\x15\u00DD\u00C1\u00B0\u00A4[\u00CA\u00CF\u0091\u00E7|g23\u00E2\x17\x15E\u00B1\u0094\u00A6\u00E9\u00B9\u00A4\t\u00F0\u00EC\u009C\u00DB\u00D1\x1F\u00F0\u008D\u00A4\u00AD\u00B9'\u00E9\u00E1[!\u00C6\u00B8jf\u0095\u00A4W`\u00AD\x17\u00BF%\x03\u00C3/%\u008D\u0080\u00D1@v\u00E5\u00FA\u008E\u00A4]I\u00EF\x03\u00FEu\u00D34{\x0B\u0085\x18\u00E3\u00B1\u0099\u009D\u0099\u00D9f\u00B7df\u00F7u]OB\b\u00AD\u00EB\u00C2\u00C0!0\x06\u00A6\u00DD\u0092\u00A4\u00E5\x10B\x0B\u00E0\x00\u00AA\u00AA:\u00FA\u0084\u00E7\x1A\x03\u00D3\u00D9l\u00B6\x0E\u00DC\u00B5m\u00BB\u00F1\u00B5ZY\u0096+\u00DE\u00FBG`\u00E1cfv\u0090\u00E7\u00F9I\u00FF\x16')\u00FD/\f\u00908\u00E7\u00B6\u00BB,\u00B0\u009F\u00E7\u00F9\u00E9\x10\f\u0090\x00)paf\u00B7\u00DE\u00FB\u0097,\u00CB\u009E~\u0082\x01>\x00G\u00B9W6\x14\u00F7K\u0085\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_eye_dropper.png", "w12" );
w12_eye_dropper;
// ==================== |----------| ====================
// ==================== | w12_menu | ====================
// ==================== |----------| ====================

var w12_menu = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u0082IDAT(\u0091\u0095\u008E=\n\x021\x10\u0085g&\x13E\b\u00AC\u008D6{3\u00DB\x04\x0Fce\u00E3\u00E5\u00BC\u0084\b\x11v\\\u00C8\u00B3\x14\u00BB\u00E7W\x7F\u00EFG\u00E4O\u00B4\u00D6z3\u00B3\x03#\x03x\u00BA\u00AA\u009E\x00\u00EC\u00C9\u0081\u00C5\u00CDlN)m\x19\u00BB\u00F7\u00FEv\x11\u0099\"b\u00C7\x04r\u00CE\u008B\u008F1\u00EEfV\u00C8K\u00AB\x03\u00B8\u00AA\u00EA\u0091\u00B1\x01<\u00C8\u00E2/\u00DAZ\u00BB\u0088\u00C8D\u00C9\u00AA/\x17\u00913\x1B\x00\x10\x1E\x11s)eC.\u00AC\u008C\u00F7\u00C3\x07<\u00B8*\"Z\u009E\x14h\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_menu.png", "w12" );
w12_menu;
// ==================== |-----------| ====================
// ==================== | w12_heart | ====================
// ==================== |-----------| ====================

var w12_heart = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x00X\x00\x00\x00X\x01x\u00DAKb\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00FFIDAT(\u0091\u0095\u008F\u00BDJ\u00C3`\x14\u0086\u009Fs\x1AH\u0083\u008BtU';z\rR\x05\x07M\u00BF!K\x06\u00F1N\u00EA\x15\u0088\u00B7\u00A1\u00C3\u00B7\u00C4\x10\x10\x1C{\x0F\x16\x05\x11\u00D4\u00E2\u00AE $\u0085$\u009FK\x02\u00A5\u00C6\u00BFgzy\u00DF\u00E7\fGhH\u00D3t\u00A8\u00AAky\u009E\u00DF\u00C6q\\\x01Xk{A\x10\u00EC\u0094e\u00F9\x11E\u00D1#\u0080$I\u00B2\u00AE\u00AA\u0097\u00C0as;\x07N\u009A|\x01l\x02\u0088\u00C8uQ\x14\u00C7\u009E\u00AA\u009E/\u00C9\x00[\u00C0M\u0093\u00FBm\u00E9\u009C;\u00F2}\u00FF\u00CC\x03\u00C6|\u00A5\u00DF\u00D1\x01\u008C\x15\u00A8\u00BF\x19\u00BB\u00A8\u00D59\u0097\u00FC\u00E3\u00E0J\u009Ds\x13`\u00F6\x07y\u00B6X,N5\u008A\u00A2\u00B7\u00AA\u00AA\u00F6E\u00E4\u00EE\x07\u00F9\u00A1,\u00CB\u00838\u008E\u00DF\u00A5m\u00B2,\u00DB\u00A8\u00EBz\nl\u00AF\u00C8/\u009E\u00E7\u00ED\u0086a\u00F8\f\u00A0mk\u008Cyu\u00CE\u00ED\x01OK\u00F2\\DF\u00AD\f \u00AC\u0090\u00A6\u00E9\x10\u0098\x02=U\x1D\x19c\u00EE\x7F\u00FD\u00CEZ;\u00B0\u00D6\x0E\u00BA\u00B6Ob\u00A5U3X1\x7Fs\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_heart.png", "w12" );
w12_heart;
// ==================== |----------| ====================
// ==================== | w12_more | ====================
// ==================== |----------| ====================

var w12_more = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u009CIDAT(\u0091\u009D\u00901\n\u00C2@\x10E\u00DF,\u008B\u00A2\u00C4X\u00A8E\u00AEf\u009D`i\u00EF\x15,,-\u0092\u00A3\u00D9\x0BJ\u00BA\x10X!\u00FBm\u0083D\x12}\u00E5|\u00DE\u00CCg\u00E0Glh\u0098\u00E7\u00F9\u00D5\u00CC6\u0092\u00EEUU\x1D\u00FB\u0099\x1F\u00DCb\u00B6\u0097\u0094\x02\x0F`\u0092\u0090y\u00EF\u00E7]\u00D7\u0085\u00CFlP\x00\u00D6!\u0084E\u008C\u00B1\x05\u00DAQ!\u00C6xs\u00CE-\u009Ds5\u00B0\u009Dr\u00E1\x02\u00EC\u0080\u00FB\u0097|:\u0083o-\u008A\u00E2,)5\u00B3\u00BA,\u00CB\u00D3h%I\x07`%\u00E9\t\u008C\x0B!\u0084,I\u0092Y\u00D34\u00AF\u00FF\u008A\u00F7x\x03\r\x1D67@\u00C0\x1B6\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_more.png", "w12" );
w12_more;
// ==================== |------------| ====================
// ==================== | w12_search | ====================
// ==================== |------------| ====================

var w12_search = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01)IDAT(\u0091\u0085\u0091\u00AFO\x03A\x10\u0085\u00BF\u00B79(\x02\x12\x12\x1C(D%\u00E0\u00EA1\b\u0092\u00F6*\u0090\x15\u0090\x00\u00A6\u00E1\x1F\u00A8&X\x1C\tI% 0\u00DC\u00B5\u0086\u0084\x10\x04\x02P5848~\x04\u00D4]\u00B8\x1D\u00CC^\u00D3\u009C\u00E1\u0099\u009D\u00CC\u00BE/\u00F3vV\x04\u00A5i\u00DA\u0091t`f+@\x0E\u00DC\u009B\u00D9a\u00BB\u00DD~bB\n\u00E6S`?\u00F4\u00BE\u0081\x19`\x1A\u00F8\x05v\u00E28>+\x01\u0097\u00A6i'\u0098\u00DF\u0080\u008DV\u00AB5\u009Fe\u00D9\u009C\u00A4.`@\x7F0\x18,\u0097@\x04tC\u00BD\x1D\u00C7\u00F1M\u00A8s\u00E0$I\u0092%I=3\u00DB\x03z\x00\x0EX\x05~F\u00A3\u00D1-\x15I\u00BA\n\u00E7\u00DA8\x12\u0090\x01\u00B5F\u00A31U\x05\u00BC\u00F7\u00B3\x00f\u00F69\t\u00DC\x03\u00D3y\u009E\u00EFV\x01\u00E7\\\x19\u00F7q\u00FC\x063;\u0092\u00B4)\u00E98I\u0092EIW\u00DE\u00FB\u00D9`\u00DE\u0092\u00F4\u00E1\u009C;\u00AF\u00AE\u00B5\x03\u00F4\u0081Ze\u00C8;\u00B0\x00\u00BC8\u00E7\u00D6\u009B\u00CD\u00E6\u00AB\u00CA\u009B\u00B0\u00BA\u00FD\u00F0q_f\u00F6\x10E\u00D1EQ\x14O@\u00BD\u0084\u00C4?\x1A\x0E\u0087K\u00DE\u00FB;\u00A0nf\u00CF\u00FF\x02%T\x14\u00C5\u00B5\u00A4\u00CB?E\u00CCr9\u00B0B\u00A2h\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_search.png", "w12" );
w12_search;
// ==================== |------------| ====================
// ==================== | w12_parent | ====================
// ==================== |------------| ====================

var w12_parent = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00EEIDAT(\u0091\u0095\u008F1K\u00C4@\x10\u0085\u00DF\u00CC.h\u0095\u00E2\x14\x14[AP\u00AE;\x0BK\u0083}6\x04l\u00FD\t\u0082\u00BD\u00BD\u00BD\u00FE\x04\u00E1\u00AE\u00DB\x10\x16b/\u0096V\"\u00F8\x0F,\x0E\x04+\u00C1\u00C4\u00CC\u00D8\u009C\u00CD\u00BAA\u009Cjx\u00EF}\u00F3\x18\u00E0\u009FC\u00DE\u00FB}c\u00CCt\x18\u0086\u00E7\u00AA\u00AA^\u00C6\u0082M\u00D3\u00EC0\u00F3\u00B6\u00B5\u00D6>\u00A8\u00EA\u00C4\x18\u00F3\x06`3\x0E\u00B6m\u00BB\u00D6u\u00DD\x05\u0080K\x11Q\u00AB\u00AA\u0093\u0095\u00B7\x11\u0087C\b'}\u00DF_\x13\u00D1\u00C1\u008FfS\u00F5u]\u00EF2\u00F3\u0095\u0088\u009C\u00C6\u00DE/ \u0084P\u008A\u00C8\x02\u00C0z\u00EA\x18\u00C7\u0082\u00AA~\x02\x18R\u00E1$P\x14\u00C5\x1D\u0080=U\u00BD\x05\u00A0\x7F\x02\x00\u00E0\u009C{-\u00CB\u00F2\f\u00C01\u0080\u00A7\x18X\u00AE\u00F6e\u00C4\u00C19w\u009Fe\u00D9\u008C\u0088\u00CE\x01\u00BC\x03\u00F8\u00B0Dt\u00A4\u00AA\u0087D\u00F4\u0098j\u00CB\u00F3\u00FC\x0B\u00C0\u008D\u00F7~\u00CE\u00CC[c\u00BF\u008D\u00CE7Q\x7FM\u00B8\u00BA\x06dZ\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_parent.png", "w12" );
w12_parent;
// ==================== |-------------------| ====================
// ==================== | w12_folder_closed | ====================
// ==================== |-------------------| ====================

var w12_folder_closed = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u0080IDAT(\u0091c` \x110n\u00DA\u00B4)\u00F6\u00FF\u00FF\u00FF>\f\f\f\u008C\f\f\f\f\u008C\u008C\u008C\u00FF\u00FE\u00FC\u00F9S\x17\x14\x14t\x0B\u00AB\u0086\u008D\x1B7~b``\u00E0E\x13\x7F\u00C6\u00C8\u00C8\x18\u00CC\u00C4\u00C4\u00F4\nY\u00F0\u00DB\u00B7o\u00CFY\x18\x18\x18X\u00B1\x18$\u00F5\u00FF\u00FF\u00FF\u00E3\x7F\u00FF\u00FEE\x11dgg\u00BF\u00CCD\u00A2\x17tI\u00D5\u00C00H5|&A\u00FDg\u0096\u00FF\u00FF\u00FF\x07011\u00D9\x12\u00A3\u00FA\u00DF\u00BF\x7F\u00C7Hu\x11\x03\x00\u00B3\u00AD&C\u00C5\u00F7F\u009C\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_folder_closed.png", "icons" );
w12_folder_closed;
// ==================== |---------| ====================
// ==================== | w12_run | ====================
// ==================== |---------| ====================

var w12_run = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00E4IDAT(\u0091\u008D\u008E=N\u00C3@\x10\u0085\u00DF\u00B3\f\u00AEpmQCI\x05\x17p\u008F<\u00B2\u00DCs\x04\u00A4H \u00AE\u0080\u0094\u008A#\u00A4\u00C4\u00D5\u00EE:tH\u00A4JK\u0087\u00F8I\u0083D\u00E1\x13 \u0090\u00D9\u009DT\u0091,Hb^\u00F9\u00CD|\u00F3\u0086u]\u00EF&I\u00F2H\u0092\u00AAz)\"w\u00D8\x12Zk\x0F\x00\u00BC\u00F5\u00D8\x14\u00C0HD\x16\u00EB\u0084(\u008A\"\u00FEb\u00A7\x00\u009E\u009Cs7\u00D6\u00DA\u00BD?\u00C2\u0086\u00E6\x1DU=\x07\u00F0l\u008C9SU\x0E\t\u00AB\u00EC\u0093\u009C8\u00E7\x1E\u008C1G\x00\u00C0\u00A6i\x0EC\b\u00AF\x03\"\x00|\u0093<\x1Ej\u00E8\u00C7\u00AB\u00EA\u00CF\x7F\u0085{\u00EF\u00FD\u0089\u0088\u00BC\u00C4\x03\u008B\x0B\u0092\u00A3\u00A2(\u00A6+\u00B0I\u00F8\x040N\u00D3\u00F4:\u00CF\u00F3\u00AF\u00FE \x0E!h\x1F\u0090\u00BC\u00ED\u00BA\u00EE\u00A2\u00AA\u00AA\u008Fu\u0097\u00E2\u00B6m\u00DF\u00B3,\u009B\u0093\u008C\u00BD\u00F7WeY\u00CE\u00B6\u00FD\u00B8\x04\u00AD\u0088P\bP\u0083\u00FB\u00B9\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_run.png", "icons" );
w12_run;
// ==================== |----------| ====================
// ==================== | w12_edit | ====================
// ==================== |----------| ====================

var w12_edit = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00BEIDAT(\u0091\u0085\u00CE\u00B1M\u00C4P\x10\u0084\u00E1\x7F\u00DE\x11\u00D1\u00C3\u00C5G\x11\u00D4\u00E0]\x11Q\x00\t\x05\u0080\u0091\b8BKdD\b\n B\u00B6\u009B\u00B8\u0094\u0094\x02\u0088\u00A0\u0087!1\u0092\x05\u00B6\u00DEd;\u00FA\u00B4\u00BB\u00A2\u0092\u00BE\u00EFo%]\u00D9n3\u00F3I\x15|!\u00E9y\x1Am\u00FBr\u00B3\u0086\u0087a\u00B8\x06\u00DE\u0081\x13I[@\u0092N\u00CB\n\u00DE\x03\u009D\u00A47\u00A0\u00B5}\u0098N<\u00FC{i\u00C2w\u00B3\u00EA\u00DB\u00F6\x19\u00B0\u00CB\u00CC\u0097M\x05\x03\x1C\u0097R>\"\u00E2\x11\u00A0T0\u00B6\u00BB\u00A6i\u00BA\u00DFY5\u009C\u00997\u00F3\u00AE\u008C\u00E3\u00D8.a\u00E0\u00FE/\x06(\u00B6\u00CF\u00976G\u00C4~a\t\x05\u00D8\u00D6\u00DE\u0098\u00E7\b0\u00F0\x05|Jz\u008D\u0088n\r\x03\u00FC\x00\u00E1\u00B1T\u00C0\u00EC\u008A\u0080\u00CF\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_edit.png", "icons" );
w12_edit;
// ==================== |---------| ====================
// ==================== | w12_add | ====================
// ==================== |---------| ====================

var w12_add = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00B8IDAT(\u0091\u00BD\u00901\n\u00C2P\x10Dg\u00B2\u00BFW\u00BC\u0080\u0085DKA\x1B/`\x15\u0084\u00DC\u00C6\x03Xy\u009B\x1F\u00D3z\x00A\u00B0\u0096\x14\u0082\u0095\u0095'\b\x7F\u00D7\u00C6h\u00F8\"\u0092\u00C6\u00ADv\u00F6\u00ED\f\u00CB\x12Q\x15E1\x01p \u00A9\"2\u00CB\u00B2\u00EC\u00DA\u00E6Il0\u00B31\u0080\u00BE\u0099\r\u00EA\u00BANc\u00FEa\u00F8U\u009D\r\u00AE,\u00CB4\u00840l\x06$\u00A7\u00AD~\u00E6\u00BD\x7F/;wq\u00AAz$\u00D9\u00FB\x12\u00B8%\u00F9\x12!\u0084[\x02@;\\d\u008E\u00E4\\UG\u00AD3\x16\x006O\u00B96\u00B3S\u00C3D\u00A4b\x1C\u00E1\u00BD_\u0091\u00DC\x01\u0080\u0099-\u00F3<\u00DF\u00B7\u00F9\x1F\u00DE\x1A\x0FD\u00E4\u00AC\u00AAw\x00AD\u00AA\u0098?\x00\rZ:Hg\x16\u008E\u00A1\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_add.png", "icons" );
w12_add;
// ==================== |------------| ====================
// ==================== | w12_remove | ====================
// ==================== |------------| ====================

var w12_remove = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x003IDAT(\u0091c`\x18\u00FA\u0080q\u00E3\u00C6\u008D\u00A9\f\f\f&D\u00AA?\u00CF\u00C2\u00C0\u00C00\u0099\u0081\u0081\u0081\u009DH\r\x7F\u0099\x18\x18\x18\u008E\u0092\u00E0\u00A2c$\u00A8\x1D>\x00\x00\u00BE\u00ED\x06\u00A5?G4\x19\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_remove.png", "icons" );
w12_remove;
// ==================== |----------| ====================
// ==================== | w12_sort | ====================
// ==================== |----------| ====================

var w12_sort = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01 IDAT(\u0091\u008D\u00911K\x03A\x10\u0085\u00BF\x1D.\u00B7Uj#\x16\u0081\u0090NR)Z\u00C76\u00C5\\e\u00A7?\u00C0\u00D2\x1F\x10\u00ACLeaei\x7F\u0082aO\u00EC\x04K\u00AB`\x17\x10\u00C1\u00C6\u00C6\u00C6.\x04C\u0096;\x0B\u00F7`9\x14|\u00F0`gfg\u00E6\u00F1\u00C6\x00\u00E4y\u009E\u00A6i\u00DA\u00CF\u00B2l\x0E0\u009DN\u00F7D\u00A4\u00CB\x0F\u0096\u00AAz\x1F\u00DE\b\u0080\u00B5\u00F6\u00CA\x18\u00F3T\x14\u00C56\u0080\u0088L\u0080<\u00B0 \u0082q\u00CE\u009D\x02\x17!~\x13\u0091}\u00C0z\u00EF;\u00A1y\u00A1\u00AA/\u00F1\u0086>0\x0F\u00F1\u00AC,\u00CB\u0081\u00F7~KDz\u00C0g\u00FC\x19@T\u00F5\x04\u00B8\x01P\u00D5CU}\u00AC%\u0089\u00C8\x11\r$\u00CDD\u0090q\u00EC\u00BD\u00EFXk_\u00FF\u00D5\x10$u\u00D7\u00EBu\u00CF9\x17\u0097\u0096\x7Fm\u0098\x00\x07\u00BF\u0094J\u00E3\u009C\u00BB\x03v\u0080Mc\u00CC\f83\u00C6<\u00D7.5\x06-\x04\u00B8\x066\x00\u00AA\u00AA\u00F2\u00EDv\u00FB\u00A1v)&\u00C11Q\u00D5\u00DB\u00AA\u00AA\u00C6\u00C0{\u0092$\u00D9p8\u00FCj\x1C.'rL\x00T\u00F5|\u00B5Z\rF\u00A3\u00D1G\u00EDRY\u0096\u00BB1[\u00AD\u00D6%\u00C07\u0093eo,\u00FFz&S\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_sort.png", "icons" );
w12_sort;
// ==================== |-------------| ====================
// ==================== | w12_sort_up | ====================
// ==================== |-------------| ====================

var w12_sort_up = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00F5IDAT(\u0091\u008D\u0090\u00B1J\x041\x14E\u00CF<w&\u00BF\u00B0`\u00B1\u00B0l'Zjo#\u008CEf>@?\u00C2\x0FPa\u008B\u00AD,\u00AC\u00FC\u00874\u00C3\u009B\u00DE\u00DABl\u00B7Y\x10\u00BFA\x16\u009B\u00C4`\u0093\u00C08\u00E8\u00EA\u00ADr_r\u00B9\u00E7\u00A5\x00p\u00CEUUU-\u009A\u00A6Y\x03t]w,\"\u00B3\x18\u00E3s\u00DB\u00B6o\f$\x00\u00C6\u0098\u0087\u00A2(\u009E\u00FA\u00BE?\x00\x10\u0091\x15\u00E0D\u00E4\u0082\u0091\nU\u00BD\x02\u00EE\u0092\x7F\x15\u0091\x13\u00C0\u0084\x10\u00A6\u00C6\u0098M]\u00D7\u00EF\u00E3\u0086\x05\u00B0N\u00FE%\u00C6x\x18B\u00D8\x17\u0091\u00B9\u00F7\u00FELU\u00CF\u00BF5\x00\u00A8\u00EA-pc\u00AD\u00CD\u00FE\x118Mo\u00A2\u00B5v/\x07&c\u00C6\u00B4\u00C3e\ba\u009A\u00CE\u00DB\u00E1\u00DD\u008F\u0081\u00844\u00CB^U\u008F\u00F2\u008F\u00FD\u00D6\u00B0\x1A \u00E5\u00D95\u00B0\u00FC\x13)\u00CB\x18\u00B3\u00F97\x12\u0080\u00F7~\u00AE\u00AA\x1F9\u00F0\t\u00C4]HIq\x02P\u0096\u00E5\u00BD\u00F7\u00DE\u00EDBJ\u00F3\u00ED\x17F{]\u009A\u00992\u00D4f\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_sort_up.png", "icons" );
w12_sort_up;
// ==================== |---------------| ====================
// ==================== | w12_sort_down | ====================
// ==================== |---------------| ====================

var w12_sort_down = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00EFIDAT(\u0091\u008D\u00D0\u00B1J\u00C4@\x10\u0080\u00E1\x7F\u00C7\r\u00A9R+X\u0088\u0096\u0082\u0095b\x1F\u00DB\x14\u00E3\x0Bx\u008Fr\x1C\bZ\u00DF\x13\u00D8[\x18\u00B2b\u00E7\x1B\u00E8\x0B\u0088\u008D`ccw\x1C\x06\u0096\x1D\u009B\u00DC\u00E1%9p\u00ABe\u00D8\x7F\u00F9\x18\x07\x10B84\u00B3cU}\x02\u00A8\u00EB\u00FA\\D\x0E\x18\u009E\u00A5\x070\u00B3\t0\x03\x1C\u0080\u0088\u00DC\x02\x17#A\u00F2\u00DD\u00C5\u00FD\u009D\u008A\u00C8$\u00C6\u00B8\u00D7\x7F-\"\x0B\u00DF\x1F\x02\u00C4\x18\u00F7\u00FB\u00A4\u0094\u00D2\u008B\u00AA~\u008C\x06c$\x11\u0099\x02\u00D7\u00DB\u0082\x01)\u00CF\u00F3w\u0080\u00FF\u0090\u0096\u00AB\u00EDm\rz\u00A4\x04\u00EC\u00AC\u0083\u00A6i\x1E\u0081S\u0080\x10\u00C2+0s\u00CE\u00ADI\"\u00B2\u00D8\u00F8\f\u00B8\x03v\x01\u00CC,\x16E\u00F1\u00DC\u0091\u008E\u0080oU}\u00DB\bT\u00F5\u00C1\u00CC\u00A6\u00C0\u00A7\u00F7\u00FE\u00B2,\u00CB\u009F\u008Et/\"W\x03.\u0080\u00AA\u00DE\u00B4m{RU\u00D5\u00D7jK)\u00A5\u00B3,\u00CB\u00E6\u00FD\u00E0\x17\u00D8\u00F2U\u00A0\u00FB \u00A6,\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_sort_down.png", "icons" );
w12_sort_down;
// ==================== |--------------| ====================
// ==================== | w12_language | ====================
// ==================== |--------------| ====================

var w12_language = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x15IDAT(\u0091\u0085\u008F\u00B1J\x03Q\x10E\u00CF{/K\u00D6\u00CE\u00CE\u0098\"\u0085\x16\u00C1*B\n\u00B1\u00D5B\u0090\u00C5\u0097BX\u00FD\x00!\u00B5\u0085?\u00E0\x1F\u0088M\u009A\u00D8\u00AF\u0090e\u0093\u00D4\u00FA\x05AD,\x15;;c\u00BB\u00C5\u00E6\u00DA\x18\u00D0\u0098\u00C4S\r3\x1C\u00EE\\\u00C37\u00BD^o\u00CB9\u00F7\x00\u00DCy\u00EF\x0FY\u0080\u009D\x0E\u00A5R\u00E9\x04\x18\x03\x07Y\u0096U\u00FF\x15$\u00C5\u0092\u00BA\u00C0X\u00D2\u00F1R!M\u00D3m\u00A0n\u00AD\u00951\u00E6\u00CD\x18\x13/\x15\u009Cs1\u0090K:\u0095\u00B4\x0E\u00EC\x0E\x06\u0083\u00DABA\u00D2\x11p\u00EB\u00BD\u00DF\u00B4\u00D66\u0080BR\u0094$\u00C9J\u00A7\u00D3\t\u00E6u\u00D8\x0B\u00C3\u00B0\r0\x1A\u008D>$yI\u008F\u00E5r\u00F9\u00BER\u00A9\\\u00A7i\u00DA\u00CC\u00B2\u00AC\x0E`f#\u0087\u00C3\u00E1FQ\x14/s\u00BEy\u00F7\u00DEW\u00ED\u00EC6\u008A\u00A2\u00D7<\u00CFW'\u0093\u00C9\u008E\u00B5\u00B6\t\\\x02\u009FA\x104\u00E6&L\u00E9\u00F7\u00FB]I\u00FB\u00C0\u009A\u00A4\u008BV\u00ABu\u00F5\u00B3\u00C3\x1F$=\x035\x00c\u00CC\u00D3l\u00E9_$I\u00E2\u00803I\u00E7\u00C0\r\u00D0\u009E\u00DE\u00BE\x00Mvj\x14\"V\u0098\u00CA\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_language.png", "icons" );
w12_language;
// normal 16px
// ==================== |--------------| ====================
// ==================== | w16_language | ====================
// ==================== |--------------| ====================

var w16_language = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x11IDAT8\u008D\u00ED\u0090\u00BDJ\u00C4`\x10E\u00CF|\u009B\u00FD@\u008B\u00B5[{\u00D1\u00DA\x05\u00C1\u009F\u00CAJ\x0BKA\u00EC,\x04\x1FA\u00D6\u00C6G\u00B0\x12}\x02m\u0082\u00A0\u00C1\u00CA\x07\x10\u00844vZ\u00BA,\"VQ\x0BCB\u00C8\u00B5\u0089KX\u00A2/\u00A0S\u00DE;s\u00EEe\u00E0\x7F,\u008A\u00A2W\u00A0\u00FB\u0083\x7F\x02L\x00\u00BBM\u00A6\u00A4\u0081\u00AB\x1D\x7F\x02G\u00C0\u00F0\u0097\u00C0\x14\u00B8\x18\u00A5\u009Bu]\u00CD\u00BC\u00CC\u00B2\u00EC\x108n8,\u00CDl\u00C39\u00B7\u009Ee\u00D9\u008E\u00A45\u00A0\x0FP\x07\u0084\u00DE\u00FB\u00BD \bB@c\x00'\u00A9/i\u00CB{\x7F\u009A\u00E7\u00F9-\u00B0Y\x07\u00BC\u00B5\u00DB\u00ED\x1B\u00E7\u00DCs\x1C\u00C7CIw\r-V\u00CB\u00B2\u00BC6\u00B3\x03\u00EF\u00FD>\u00B0\x04\x10T\u00CF\u00B8*\u008AbV\u00D2v\u00AF\u00D7K\u009Ds\u00A1\u00A4\u00951\u00C0{\u00AB\u00D5\x1AH\u009A\x04\x1E\u00BE\u00C5\u00A0z\u00C6\u00B2\u00A4s`\x1EX\u0094\u00F4\u00D2\u00D0`J\u00D2\u0099\u00A4\x053\u00BB\u00974\x02<\x02\u00D3\u00D5Rbf\x1D\u00A0\x03$\u0092R3\x13\u0090TM\u00E7\u0080\x0FI3\u0095\u00F6\u00D4\x10\u00F4\u00E7\u00E6\x0B\u009C7p\u0085\x04}\f\u00FE\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_language.png", "w16" );
w16_language;
// ==================== |-------------------| ====================
// ==================== | w16_settings_file | ====================
// ==================== |-------------------| ====================

var w16_settings_file = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01JIDAT8\u008D\u0095\u0092=K\x03A\x10\u0086\u009F9\x0E\x16s\u0095]\u00B4\x10,\u00B4\u00B5\u00B1\x16R\tB\u00B2]\u008AT6\"VZ\u00F8'l\x15A\u00FC\t\u0081\u00C0\r1\u00B6~T\u00B6\u0096b\nA\u00A2\u0085\u0082\u0090\"p\u00C7\u0085\u00B5HN.\u00F1\u00F2\u00F5V\u00B3\u00B3\u00EF<\u00CC\f#\x00\u00F5z}\u00C9\x18\u00D3\x06V\u00F9\u00AF\u009F(\u008A\u00D6\u008D1\x0F\u00C0\u009E\u00B5\u00F6#\u00FB\u00E9\x01\x18c\u0096'\x14g\u00B5\x05\u00DC\u00AB\u00EA\u0088\u00CF\u009BQ4\u00AE\r\u00E0.\x0BY\x14\x00\u00B0\t\u0084\u00E9\u00C3\u009F`ze0R\x00\x04\u00C6\u0098\u00C0Z+yF\x01\x18\u00B6\u00D4\x19\u00E6\x12\x11\u00D9\x15\u0091\u00A7~\u00BF\x7F)\"\u00FB9u=km\u00907\u00C23\u00E0;\u00E7\u009A\u00C0Z\x1C\u00C7\u0087\u00C0\u00F7\u00B4yF\x00\u00CE\u00B9\x1Ap\u00E5\u009C\u00FBr\u00CEE\u00D5j5\x06\u00DE\u00A6\x01\u00C6wP\u00B2\u00D6\x1E\u008D\u00E5\x1E\u0081\u00ED\u00B9:\x10\u00913U=h\u00B5Z\u00C5F\u00A3\u00B1\x02P\u00A9TN\u009Ds\x17s\x01\x18l\u00FD:I\u0092O\u00DF\u00F7\u00DB\u00AAZ\x12\x11g\u00AD=\x16\u0091\u00F3y\x00Y\x15\u0080\u009B\x14R.\u0097O\u00F2 \u00B3\x0E\u00A9\x004Uu'\u0085\x00/Y\u00C3\u00A4C\u00CA*\x00nU\u00B5\x16\u0086a\u00C7\u00F3\u00BC\u00E2\u00A2\u0080\x14\u00A2\u009E\u00F7\u00D7p/\r<\u0080(\u008A\u00BA@wN\x18\u00C0{\x1A\u00FC\x02\x17\x16i\u0080-\u00A9W\u00C4\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_settings_file.png", "w16" );
w16_settings_file;
// ==================== |----------| ====================
// ==================== | w16_user | ====================
// ==================== |----------| ====================

var w16_user = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x1BIDAT8\u008D\u009D\u0092?K\u00C3@\x18\u0087\u009F\u00F7\u0092\u008F\u00D0\x16\u00C1Epw\u00D5\u008E.\u008A\u00C14$\u009FE\x10\x1CT\x1C\u00A4\u00F4\u00C3\x044\u0090A\x14\x1C\u00DD\u00C4\u00D5\u00A9\u008B\u00A0\x16\x04\u0087\x0E\u00CD\u009F;\x07\u00DB\x1A\u00D2\u00D4\u00B4\u00F9\u00C1-\u00EF{\u00CFs\u00C7\u00DD+T$\u008A\u00A2\u0096\u00D6\u00BA\x0F\x1C\x02\"\"w\u00B6m\u009F:\u008E3*\u00EF\u0095r!\u008E\u00E3v\u009A\u00A6\u00CF\u00C0f\u00A9\u00F5\u00AE\u00B5\u00EE\u00FA\u00BE?,\x16UY\u0090$\u00C9\u00A0\x02\x06\u00D8PJ]\u0097\u008B\x0B\x02\x119\u00AA\u0080g9\u00A8\x15\x00\u00D6?\u0082\u0085^\u0095\u00E0~\x19m\u008Cy\u00A8\x15(\u00A5ND\u00E4\u00AB\u0082\u00FF\u00D6Z\u009F\u00D5\n\\\u00D7}\u00CB\u00B2\u00AC\x0B\u00DC\x00\u00E3\u00E9\u00BAUJ\u00ED\x05A\u00F0\u00BA\u00ECv\u008D3\u009F\u00830\f;\u00B6m_\x19cz@\u00C7\x18\u00F3(\"/\u0080\x01v\u0080}`\x04D\u00C0\u00B9\u00E7y\x1Fs\u00C1t\u00F2\u009E\u0080\u00ED\x15\x0F\x1E\u00E6y\u00BE\x1B\x04\u00C1\u00A7\x02\u00D0Z_\u00AC\x01\x03lY\u0096u\t\x7F\u008Fx\u00BC\x06<K\u00AF(h5\x10\u00B4\u008B\u0082I\x03\u00C1\u00A4(\x18\u00F0\u00FB\u00DF\u00ABf,\"}\u0080\x1Fd3T}\u0091\u009Et \x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_user.png", "w16" );
w16_user;
// ==================== |---------| ====================
// ==================== | w16_bug | ====================
// ==================== |---------| ====================

var w16_bug = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\u009BIDAT8\u008D\u008D\u0092\u00B1\u008B\x13Q\x10\u00C6\x7F\u00F3\u00DE.\u00C6\u00E6\u00AC\x14\u00AC\x15,$J\"\u00D8\u00E8\u00A1x\u00DD\u00EE\u00C5\u0084-\u00F2Oha#X\u00A6\u00F4/\u00D0\u00BF\"\u0081\u0087\u00F2\x04\u00D1\u00A0\u00DC]\u00A5\u008D\u00DC\x156\u0087\u00D8\x1D\u008A\u00CD\u0081hn_\u00DE\u00D8\u00E4\u0096\u00B0\u00EC\u00E1~\u00D503\u00DF\u00F7\u00CD\f#4\u00C0{\u00BFQ\u0096\u00E5s\u00E0!`E\u00E4M\b\u00E1iQ\x14\u00BF\u00EA\u00BDROL&\x13\u00D3\u00EB\u00F5\u00E6\u00C0\u00BDZ\u00E9\u00CBb\u00B1\u00B8=\x1E\u008FO\u00D6\u0093\u00A6.\u00D0\u00EF\u00F7\u00EF7\u0090\x01nv:\u009DA\u00E3\x04\u00CE\u00B9\x1D\x119\x1FB\u00C8\u00D24\u00BD\u00B2\\.\u0087M\u00AB\x19c\u00DE\u0086\x10\x0E\u0092$\u00F1\u00AA\u00FAg8\x1Cn&\u00AB\u00DA9U\u00BDe\u00AD\u009D\u00C7\x18\u00B7F\u00A3\u00D1\u00B3&\u0081\u00D9lv\u00C9Z;W\u00D5\u00EB\u00C0g\u0080\x04 M\u00D3\u00BC,\u00CB\u00F7@\x178r\u00CE5\u00F1\u00D7\u00F15I\u0092\x01\u00ACn\u0090e\u00D9O\x11y\u00F2?\u00D6\u00DA*\u008F\u00F3<?\u00AA\x04VX\u00D6\x1BU5\x17\u0091\u00AC\u009E\x0F!T\u00BD\u00E2\u009C\u00DB\x01\u00EE\u009Ea\u00F6@D\u00A2\u00AA~8\u00A3\u00BEgD$\u00B4\x1D\u00BD\x01\u00A1z$\u00E7\u00DC\x1D`\u00B7\u00CD\x04\u00C6\u0098\u00CD\u00C1`\u00B0\x0Bk7\b!|\x07\u00B4\u00A5\u00F3\u00B7J\u00EC4\x10\u0091\x13\u00E0\u00B8\x05\u00F98\u00C6X\u00ADm\x00\u00BC\u00F7\x17\u00AD\u00B5\u00EF\u0080\x0B-\x046\u0080\u008F\u00D3\u00E9\u00F4r%P\u0096\u00A5\x07\u00BA\u00AAz\u00A0\u00AA7T\u00F5\x05p(\"1\u00C6\x18\u0081C\u00E0\u00A5\u00B5\u00B6\x0B\u00EC\x03\u00D7\u00D24}\x05\u00ABO\x04~\x03\u009Fb\u008C\u00DBEQ\u00FC\x00\x1E\u00D5\\\u00AF\u009E\x06\u00DE\u00FB\u00AD\x10\u00C2kU\u00FD\x0B\u00F0\x0Fk\u009B\u00A0\x10\u009B\u00CE\u00DB\u00A2\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_bug.png", "w16" );
w16_bug;
// ==================== |------------| ====================
// ==================== | w16_rookie | ====================
// ==================== |------------| ====================

var w16_rookie = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x00\u00F5IDAT8\u008D\u00DD\u00921N\x031\x10E\u00FF\u008C\u00B1\u00BD\x12\x1D\x14\u00D4Pp\x05n@\u00B4\u00ABu\u00EB\u00823\u00E4N\u00D0\u0081\\lOI.\u00C0\x01\u0090(\"%\r\x01\u00CA\u0095!\u008CiR$Y\u009Bl\u00CD/\u00FF\u00BC\u00F9\x1A\u00CD\f\u00A1\u00A0\u00AE\u00EBN\u0095R\u00CF\x0043_9\u00E7\u00E69\u008EK\x01UU1\x11\x1D\x03\u00B0\x00~J\\1\u00A0\u00AE\u00EB\u0095\u0088\u00CCRJOm\u00DB.K\x1C\u00E5\u00CC\x10\u00C2\u00891\u00E6\u009E\u0088\u00AE7\u00D6c\u008C\u00F1\u00C6{\u00FF1j\x02c\u00CCt\u00AB\x19\x00&\u00C6\u0098i\u008E=*L\u00F6\x0E\u00E03\u00E3\u008D\x0B\x10\u0091\x07\u00AD\u00F5BD4\x000\u00F3w\u00DF\u00F7\u00B3\x1C;\u00D8A\bAYk_\x00\u009C\u00EF\u0095^c\u008C\u0097\u00DE\u00FB\u009D\u008B\fv`\u00AD=\u00CB4\x03\u00C0\u00C5\u00A6\u00B6\u00A3A\x003g/S\u00AA\x15\u00FF`\u00AC\u00FEc\u0080R\u00EA\u00EB\x0F>\x1E\fh\u009A\u00E6-\u00A5t\x07`\u00BDe\u00AF\u0089\u00E8\u00D69\u00B7\u00DA\u00E7\x7F\x01\u008D\u00CA@c^Wf\u00EE\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_rookie.png", "w16" );
w16_rookie;
// ==================== |--------------| ====================
// ==================== | w16_standard | ====================
// ==================== |--------------| ====================

var w16_standard = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x14IDAT8\u008D\u00AD\u0090\u00B1J\u00C4P\x10E\u00EF\r/E\x14\x11\u00EB\u00E0G\u00F8\x07j\u00A3\u00A4H\u00D2+X\u00A6\x10\x11\u00B5\u00B1L-\b\x16\"\x04\u00ACm\u0084%oI\u00E1\x0Fh\x15\u00F4\x03,l\u0084\u00D4\u00E2\u0082\x04\u0097\\\x1Bw\u00D9,\u00C8n\u00D4\u00A9f\x06\u00CE\u0099\u00CB\x00\x7F,\u008E\u009A,\u00CB\\\u00DF\u00F7Wg\x01M\u00D3\u00A8\u00AA\u00AA\u00D7$I>[\x02k\u00ED9\u0080\u00E39\x0F\u00D7\x00\u00AE]\u00D7=1\u00E3(\u00E4\u00A2\u00A4Y\u00A0$\u009D\x01X#\u00B9?\x1C\x0E\u00DF\u00CD,bZ\x10\u00C7\u00F1\u00A9$Zk\u00EF\x01\u00EC8\x1D\x05\u00A3\u00B4\"\u00F9\x01`\u00B9k\x02\u00E6y\u00BE\u00E58\u00CE\u00BA\u00A4M\u00927\u009D\x05$\u00EF\u00BE\x7F\u00F5@\u00F2p,\u00904\u0098C0\x00\u00B0\x07\u00E0%\u008A\u00A2'\x00\u0098\x14\u00F4H\x1E\x01\u00F8\u00F1/$o\u00C30\u00EC\u00B5v\u0093\u0083\u00B5v\x03\u00C0.\u0080\u00A5)V$\x1F\u008D1\x17A\x10\u00D4-A\u00BF\u00DF?\x00\u00B00G\u00FC\u00B6QR]\u00D7WFR\x00`\u00BB\u00AB\x00\u00C0\u00B3\u00E7y\u0097\x04\u0080\u00A2(V\u00BA\u00D2eY\u00BE\u00A5i\u00DA\u00FC\u00E2\u00F0?\u00D7\x17=\x05_\u00E7\u00F7_.`\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_standard.png", "w16" );
w16_standard;
// ==================== |------------| ====================
// ==================== | w16_expert | ====================
// ==================== |------------| ====================

var w16_expert = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07`\x00\x00\x07`\x01G\u0084ZR\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x00IDAT8\u008D\u00C5\u0092\u00B1J\u00C4@\x14E\u00CF\x0B\u008B\u0084\u00FD\b;;k\u00F1\x03,l\x02CZ+\x0B+\x0B+E\u00D1\u00D2Fp\u00F1\x07\u00FC\x02\u00AB$*\u00C8\u00B6\u0082\u00B0`)h\u00A1\u009D\u009D\u00F5J\u009A\u00CC\u00B5I`v@6\u00B1\u00F1V\u00EF\u00DD\u00CB=\u00BC\u0081\u00B1\u00AA\u00AAf\u00926X\u00AEoI\u00DB\u00CE\u00B9\u00C7\u00D0Lz\u0096\x01\u00C6f\u00B6\x1E\u009BV\u0096\u00A5z\x02\x00\u00DE\u0080O\u00E0+I\u0092\u009B,\u00CB\u008A\x05\u0080\u00A4\u00A9\u0099%\u00C0\x160\x07\u00AE\u0081\u0083\u00DFhf\u00B6\u009FD\u00C6-p\u00D7\u00AE\u00EFi\u009A\u009E\u0086\u00B9\u00A4\u00A9\u00A4\x0B\u00E0\x19\u00C0{\x7F\u00B4\x00X&3+\u009Cs\u00C7\u00C0\u00A4\u00DDW\x07\x01\u0082K\u00BAg\u00DB\u009F\x00\u00A1\u00FE\x1D\u00A0\u00D1\u00C0\u00C2JQ\x14\u00BBfv\u00D2\u00EE\x1FC\x01\u0097f\u00D6u\u00BC\u00A4\u00B3\x18\u00B0\x03X;\u00AF\u00D5u}\x15\u00E5#@\u00C0\u0083\u00A4s\u00E7\u00DCS\f\u00D8\f\u00E61\u00B0\x17\u00E5\u00F7M\u00D3\x1C\u00E6y\u00FE\u00DA\x19C?R\x15\u0096;\u00C0\u00ACg\x7F\u00EE\u00BD\x7F\u0089\u00CD\x1FA\x0EY\u0090\u00AEi\u0085\u009B\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_expert.png", "w16" );
w16_expert;
// ==================== |-----------------| ====================
// ==================== | w16_placeholder | ====================
// ==================== |-----------------| ====================

var w16_placeholder = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x017IDAT8\u008D\u0095\u0093AN\x021\x14\u0086\u00BF\u00D7\u00E2\x19p\b\t;0\u009C\u00C0\u009D\u008A\x1E\u00C0\x13hf`H\u00E0B\u00930q\u00B8\u0088\u00A2\u00EE<\u0081\x11v$\x06\u00C2!\u00A6u\u00D3!\u00A5 \u0084\u00B7j\u00DA\u00FE_\u00FF\u00FF\u00BDT\u00FA\u00FD\u00FE\u00A3\u0088d\u00C0%\u00E7\u00D5\x1A\x18\u00D5\x02\u00F1/\u00B09!\u00AC\x03M \x02\u00B2Z\u00F0\u00F2EY\u0096O\u00D3\u00E9\u00F4\u00FB\u00902M\u00D3\u00B6\u00B5\u00F6\u00DD\u00DB\u008A\u0094[,\u009D\u00A5\u00BA\u00D6\u00FA5I\u0092N(\x1E\x0E\u0087W\u00D6\u00DA\x0F\u00A0\u00E1\u00EE.\x01*\u00C0ZDn\u0081\x15\x10)\u00A5>\u00E38\u00EE\u00FA/\x1Bc\u00DE\u009C\u00EDMY\u0096\x0F\x0E\u00B2\x05\u0090\u00E7\u00F9BD\u00EE\x1C\u00A4\u00AE\u00B5\u009E\u00C5q\u00DC\u00F5l7\u009C\u00B8\u00E7G\u00DC\x02*\u00881\u00A6\u00E7\u00C7\u00F1m\x1Bcn\u00C2\u00FE\u00EC\x00\x00\u008A\u00A2\u0098\u00FBq|\u00DBEQ\u00CC\u00C3\u00FB{\u0080sk\x0F\u0090$I'\u00E8\u00F6\u00D1\u00E9\u00EC\x00\u00D24m+\u00A5f\u00BE\u00EDc\u00D3\x01\u0090\u00C1``\u0081/\x11y\u00FE\u00AF\u00DB\u0087&\u00A1\u00B5~\x01\u00AE+\x07\u00D1\u00B1n\u00E7y\u00BEPJ\u00DD\u00FBq\u009C\u00CBm\u0084\x16'\u00BA=\u0099L~\u0082\u00E9\u00B4\u00AA\b\u00D5\x06\u009C\u00F7\u0099\x00V5`\x04d\x0E\u00D2\u00F4\x0EO\u00D5\x1A\x18\u00FF\x01 \x03\u00A8.\u00A9X\u00C8\t\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_placeholder.png", "w16" );
w16_placeholder;
// ==================== |--------------| ====================
// ==================== | w16_download | ====================
// ==================== |--------------| ====================

var w16_download = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x0FIDAT8\u008D\u00DD\u0092=N\u00C30\x18@\u009FqEB\x15%\x0B\u00E2\x0E\u0094\x03\u00D0\u00D5\u00EA\x16\t\u0085\b)\x03;\u00A7\u00E2\x06\x11K\x12\x162\u00B1 \u00B8\x00\u00D9\u00B8\x00b.5\u0092m\u0096\x04\x15\u0093\u00A2\u00B0\u00F2-\u00DF\u008F\u00BE\u00F7lK\x16\u00EC\u0088\u00AA\u00AA\u009E\u0081E\u00DFvY\u0096\u009D\u008C\u00ED\u00ED\u00ED\x12l\u00C1~=Y0)\u00FE\u0081@\fE\u00D34+km;Aj\u00AD\u00B5\u00AB<\u00CF\u00EF\u00BF\u00DD \u008A\u00A2\x07\u00A0\u009Dph\x1B\x04\u00C1\u00E3\u00D0|\t\u0094R\x1B\u00ADu\x06\u00DC\u00FE\x02\u00DF\u00C5q\u009C\u00A7i\u00AA\x7F\b\x00\u008A\u00A2\u00F8\u00D0Z_\b!\x1A\u009Ft\u00CE\u00B5q\x1C\u009F+\u00A56\u00DBs\u00E1/\x02\u0094e\u00B9\x1F\u0086\u00E1\u008Ds\u00EEl\u0080\u0093$\u00C9|\x18@\u00D4u\u00DD9\u00E7\u008E\u00BD\u00F9\u00931&\u0093R^\x03\x18c\u00AE\u00A4\u0094\x15\u00B0\u00F4\u00F6\u00BA\u00D9\b\f\u00B0\u0094R\u00D6R\u00CA\u00CB\u00BE\x1F\u0083\x01\x16\u00B3\u00B1'\u00F4qj\u008Cy\u00F1f\u00EF\u00C0\u00C1V\u00FE\u00D3GZk\u00AD\u008F\u0080\u00B7>\u00AF\x07A7Q0\x0F\u0082\u00E0\x158\u00EC\u00F3\x1C\u00E8>\x01\u00D4u]l6\u00FC\u00EA\u00B8\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_download.png", "w16" );
w16_download;
// ==================== |----------| ====================
// ==================== | w16_list | ====================
// ==================== |----------| ====================

var w16_list = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01NIDAT8\u008D\u00AD\u0092\u00BDJ\x03Q\x10\u0085\u00BF\u00B9\u00DC]\x05+\u00FF@l\x04Q\x1B\u00F5\x01\x04kAp\t\u00C1*h\u0099G\b\u00A6\u00B1\x13\u00C1\u00C62/`%\u00C1\u00E2&&El\u00D2\u00D9\u00F8\x12\u0082\u0088X\x05\u0092&\u00D9\u00DD\u00BBc\u0093\u0088V\u00BB\u00A0\u00A7\x1A\u00CEp\u00E6\u009C\x19\x06\u00FE\b\u00E9t:{\u00DE\u00FBk`\u009CeY\u00BD\\.\u00BFv\u00BB\u00DD\u008D8\u008EW\u00F2\u00C4A\x10\f\u00AC\u00F7\u00FE\x1E\u00D8\x050\u00C6,\x02Gi\u009A>\x1Bc\u00D6\u00F3\x06x\u00EF\u00C7\x06X\u00FB\u00C1\u00CD\u00EA\u00B9\u0082\x1B\u00CC[\x11\u00A9\u00ABj\x03H\u0080K\x00\x11\u00A9gY\u00B6\u009D\u00A76\u00C6\u00BC\x03\u00D0l6\u0097z\u00BD\u00DEBA\u00D7_\x10\u00E7\\UD\x1A@b\u008C9\u008F\u00A2\u00C89\u00E7\u00AA\u00C0V\u0091\x04VDn\u0080\x10\b\u00BD\u00F7W\u0080\u009Br\u00CBy\x03T\x15\u00A3\u00AA\x1F\u00DFqDf\u00F5\u00A4\u00E0\x06ck\u00AD\u00AD\u00CC\u00FE\u00C0Z{1\u008Dv\u0090\u00A6\u00E9j\u009E:\b\u0082\u0081\u00F4\u00FB};\x1A\u008DJ\u00C0$\u008A\u00A2\u00AE\u0088hAw\x00\u00ECp8\u00BC\x03*\x00\u00EDv\u00FB\x16\u00A8\u00B5Z-\x07\u00EC\x17\u00D0\u00BFY\u00E0\u00E4\x07\x11\x015\u00E0\u0090\x02G\x046\u00AD\u00AA>\u0089\u00C8)\u0080\u00AA\u00F6\u00A6\u008D\x17`'O\u00AD\u00AA\u009F6\f\u00C3\u00B3$I\x1EUu\x1C\u00C7\u00F1\x03@\u00A9T:.\u00E0\u00FE?\u00F8\x023\u009D}\u00CA\u0097\u009C\u00BE\u00A0\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_list.png", "w16" );
w16_list;
// ==================== |-------------------| ====================
// ==================== | w16_language_file | ====================
// ==================== |-------------------| ====================

var w16_language_file = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\u00AFIDAT8\u008D\u008D\u0093\u00BF\u008B\x13A\x1C\u00C5\u00DF\u00F7\u009B%\u00B9\u00B5H\x1A\x11\u009B\x13\x7F\x1D\u008A\u008D\u00E4\u00AE\x11\u00F4\bil\u00CE\u00CCv\u00F1/\u00B0\u00B0VKq\u00AD\u00AE\x10\x15\x1B\u00FD\x03\u00EC<%\u00CC\u00AE\u008D\u00A0\x06\x0B\x11\u00AC-\x14\u00C1\x1F\u00E0\u00C1q)\u0084\u0080\u00C9\u00C6]\u00E6Y\x18a]\u00C7\u00E8\u00EBf\u00DE\u00F7}f\u00DE\u00C0\b\x00\u00C4q\u00AC\u00EDv\u00FB-\u0080\x15xd\u008C\u00D1$I\u00DE\u0088\u00C89c\u00CC\u00C7\u00B2\u00A7\x00\u00D0\u00E9t\u00EA\x7F\x0B\u0097t\u008C\u00E4\u008B\u00C1`p\u00E4\x0F@E\u00DF\x00\u00DC\x04\u00F0\u00D4\u00E3-\u00AB\u00EA\u00F3$I\x0E-\x02<4\u00C6\\q\u00CE]\x00@\u008F\x7F\u0080\u00E4\u00938\u008E\x15\x00\x04\x00\u0086\u00C3\u00E1\u00D2x<\u009E\x02\x00\u00C9\rUUc\u00CCck\u00ED+\x00\u00A7\x00\x1C\u008F\u00A2\u00E8\u009D\u00AFW\x15\u00F0u6\u009B\u00EDo4\x1A\u00F7I^W\u00D5\u00B3$o\u00FB\u0082\u00CDf3\u00ECv\u00BB\u00D9o\x15D\u00E4Q\u00BF\u00DF\u00FF^\x14\u00C5\u00A50\f?\u008B\u00C8\x16\x00\u00E7\x03x\u00DF\u0080\u00E4\u00834MW\u0083 \u00B85\u009DN\u00AF\u00F6z\u00BDm\x00/\x17\x01\u0082\n`\u00CD9w\x06\u00C0\u0086\u0088\u00E4\u00D6\u00DA/\u00F3C\u00EE\x00X\x05\u00B0\u00BE\x10 \"\u009B\u00A5\u00E5.\u00C9\u00F7\x00\u00E2z\u00BD\u00FE:\u00CF\u00F3}$\x0F\u008A\u00C8\n\u0080\u00BB\u00DE\n\x155\x00\u00B4Uu\u00AD(\u008A\u008B\x00NDQ\u00F4\u008C\u00E4\u00E1\u00F2\u00D0\"\u00C0^\x11\u00D9$\u0099\u0091\fk\u00B5\u00DA\u00874M\u008D\u0088\\\u00FE_\u00C0\u00AFZ'\u00A3(\u008A'\u0093\u00C9'\u0092\u00F7\u00AA\u00FE\u00BF\x00\u0099s\u00EE\u009A\u00B5\u00F6h\u00AB\u00D5\u00A2\u0088\u00C4\u00D5\u0081\u00C0\x13*kIUo\u0090\u00ECeY\u00B6\r`\u00D7{\u0083\u00D1h\u0094\x03\u00D8\u00F1\x11H\u009E\x07\u00B0\x07?\x7F\u00EB\u00E9\u00F9\u00F6\u00CE<\u0083\x1F\r\u0084\u00AE\u00AD\u00A7\u008B\x19\u00DD\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_language_file.png", "w16" );
w16_language_file;
// ==================== |------------| ====================
// ==================== | w16_random | ====================
// ==================== |------------| ====================

var w16_random = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\u00B1IDAT8\u008D\u00BD\u0093?kTA\x14\u00C5\x7Fwf]\u00FF\u00B4\",\u00C1\u00D6\u00C6\u00C6\"_ B\u008A\u0080\u00FB\u00DE3\u00AC\u00BER,\u00D4-$\x16b\u0093\u00A0\u008DH\u00CA\u00A06\u00B2\u008D\u00A0M\u00C2\x12x\u00CC[XP\u00B0\u00B4\x10R\u0089\x16\u00B6\"\u00FA\x05B\u00C2\u00DBq\u00E6Z\u00E4\x05w\u0097]D\x0BOw\u00EF\u009C{\u00EF\u0099;g\u00E0\x7F\u00A3,\u00CB;\u00CE\u00B9\u00F5\u00E3\u00D8\u00FCC\u008F\u0093\u00C0\u00A6s\u00EE\x11\u00808\u00E7*\u00A0\t\x1C\x02\x1FE\u00E4Y\u009A\u00A6\u00DB\x00\u00C3\u00E1\u00F0\u009C\u00F7\u00FE1\u0090\x00\x0B\u0080L5\u00DB\x10\u00E7\u009C\u00CE\u0098\u00F2\u00C4Z\u00BB\x15B\u00F8\x00\\\u0098'EU\u00DF6b\u008C\u0097E\u00A4\x01\u009C\x15\u0091[\u00C0\u00B2\u0088\\\n!<\u00AF\u008B\x0F\u0081M`OU\x7F\u008A\u00C8U\u00E0\u00AE\u00AA\u00BE\u00B3\u00D6\u00AENH\u00EA\u00F5z'Z\u00AD\u00D6{\u00E0%\u00F0\u00A2N\u00DF\u00CC\u00B2\u00EC\u00D51\u00A7,\u00CB\u00B5\x18cf\u00ADM\u0093$9\u0098Xb\u00B7\u00DB\u00F5\u00C6\u0098UU]\u00AES\u009F\u00D34}=\u00CE\u00F1\u00DE\u00EF\u008EF\u00A3$I\u0092\x03\u0080\u00C6\u00F4\u00BDb\u008C\u00A7k\u0099\u00A8\u00EAS\x11\u0099\u00D8Q\u00A7\u00D3\u00F91\x1E\u00CFz\u00C6\u0087\u0080\x05\u00BE\x01\x0BEQ,\u008E\x1F:\u00E7n\u00CF\u00F4\u00C1`08\u00E3\u009C\u00BB\x0F\u00DC\u00A8\u00A7o\u0089\u00C8=c\u00CCvQ\x14\u008B\u00AA*\x00\"r\u008A#\x1F\u00AC\u00C3\u00A4\x0F\u00C6\u00F1\u00C6Z\u00FB \u0084\u00B0\u00C7\u0091q\u00E6a\u00C3L\x15G`\x07\u00B8\u00DEn\u00B7?\u00A9j\x1B\u00F8:\u00AFZU\u0097\u00A4(\u008A%\x11i\u0088H\x00\u00BEdY\u00F6}\u009C\u00D4\u00EF\u00F7m\u00B3\u00D9\u00BCh\u008C9\x0FXU]\u00E1\u00B7\x0F\u00D2ik\u00FE\x11eY\u00AE\u00A9\u00EA\u00B5\u00AA\u00AA\u00AE\u00E4y\u00BE\u00FF\u00D7\u009F\u00C9{\u00BF[U\u00D5J\u009E\u00E7\u00FB\x00\u00BF\x00\u0081\u00B4\u00AF\u008D(\u008C\x1F\u00E1\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_random.png", "w16" );
w16_random;
// ==================== |-----------------| ====================
// ==================== | w16_eye_dropper | ====================
// ==================== |-----------------| ====================

var w16_eye_dropper = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01[IDAT8\u008D\u0095\u0091/L\u00C3@\x14\u00C6\u00BF\u00F7h:\u00E4\x04\x02\u008D%\u0081\u00D4\x132\x10\u00A0\u00BAsud\u00B8\u008AI23B2\u00C6\x02\t\x02\u008B \x19\n\x1C\u00A2\u0095\u0090\u00B0`\x118\u00D4\fS+\tf\u008A\\X\u00FA0W\u00D2\u0094]\x19\u009F\u00B9{\x7F~\u00DF\u00BB?\u008495\x18\f\x16'\u0093\u00C9\t\x11\u00ED\x01H\u0089\u00A8?\x1E\u008F\u008F\u00E9\x1FpDD;\u00F9<\x11u\u00AD\x06\u009DN\u0087=\u00CF\u00DB\u00D0Z\u00BF\u00B8\u00AE{W\u0084\x01@DFN\t\u00DC\x07\u00D0\u00A8T*\x1F\x00\u0096l\u0083xV\u00D2\u00F3\u00BC}\x00\r\x13Za\"\u00BA\u009Ei \"\x0F\"2\u00B2\u0081FQ\u0092$\u00A73\r\u0098y\u009D\u0099\u00B7JL\"\u00ADu\x10\u0086\u00E1\u00D7B\u00B1\x12\u00C7qOD.\x018\u00CC\x1C\x02P\x00\u00AA\u00B9\u0096Dk\u00BD\x19\x04\u0081\x06\no`\u00E0C\x136E\u00E4\u0080\u0088\u00B6\x01\u00BC\u00E5\u00DA\u0096]\u00D7]\u00CB\u0082\u009F_\u0088\u00A2\u00A8\u009B\u008335E\u00C4u\x1C\u00A76\u009DN\x1F\x01\u00AC\x00\u00B8RJ=g\rd\u00E0\x16\u0080s\u00CB}\u00C1\u00CC\u00BB\x00^\u00D34\u00AD\u00D5\u00EB\u00F5[\"\u0092\u00E2\t\u00DA6\x18@\u00DB\u00F7\u00FD{\u00B3\u00BF\u00F9en\u00D6j\u00B1`t\u00A4\u0094:+1\x07\u00C7q\u00BCj\u009B\u00AC\u0094\u00EA\u0095\u00C1\x00\u00C0i\u009A^\x14r\u00EFD\u00D4\u00FCkr&\u0087\u0099\u009FD\u00E4\u0093\u0099[\u00BE\u00EF\x0F\u00E7\u0081\u00F2\u00FA\x06n\n\u0081\u00C01\x12\x16f\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_eye_dropper.png", "w16" );
w16_eye_dropper;
// huge
// ==================== |---------------| ====================
// ==================== | w320_progress | ====================
// ==================== |---------------| ====================

var w320_progress = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x01@\x00\x00\x00\f\b\x06\x00\x00\x00\x13}d\t\x00\x00\x00\tpHYs\x00\x00\x0E\u00C3\x00\x00\x0E\u00C3\x01\u00C7o\u00A8d\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x04GIDATh\u0081\u00ED\u00DBQ\u0088TU\x1C\u00C7\u00F1\u00EF\u00FF\u00EC\u00E4\u00D8\u00AEhnV\u0096\x05E\x05\x15FB\x11\u0088\u00A2\u00AD\u00B0\u00A0ms\u00CFL\x1B\u00EB\u00FAf`ld&\x04B\u008B\u00F8\x12eBH\u00E1\u0083\u0094`\x10&\u00C9\u00E2\u00CC\u00EC\u00DD\u00BB\u00AEm`Z\b\x06>\b\u00F5\u0092\u00A4$\x158[`\u00B9-8\u00B1s\u00CF\u00BF\u0087\u0099\u0085\u00C9v\u00B7\u00BD\u00A3/M\u00E7\u00F3t\u00EF\u00B9g~\u00FC\u00EF\u00CB\u009Fs\u00E7\u009E+4 \u008A\u00A2%\x0B\x16,\u0098\u0098\u0098\u00980\u00CE\u00B9\u00BD\u00C6\u0098\u00A3\u0099L\u00E6D#YCCC\u009B\u00CB\u00E5r\u00B1\u00A7\u00A7\u00E7*\u00C0\u00E8\u00E8h[\u00B9\\\u00CE\x03-\u00C0\u00F3\u00D6\u00DA?\x1A\u00C9\u00F5<\u00AF9\u00E4\u00F3\u00F9\u00BB\u00BB\u00BB\u00BB/\x03\u0084a\u00F8\u00A9s\u00EE\u00C3\\.w\u00EAfdK\u00D2\x1F\f\f\f\u00DC\u009AN\u00A7\u00F7Zk_\u00A9\x15\u00F4\x10pZDN\u00C5q\u00FCr.\u0097\u00FB}.9\u00AA*a\x18n\x12\u0091\u00F7\u00813\u00CE\u00B9\u00D7[[[K\u00E5ry\x04x\x18p\u00C0%`\u0083o\u0082\u009E\u00F7\u00FFt\u00EC\u00D8\u00B1\u00A5\u0095J\u00E5\x02\u00B0\x02x\x00\u00F8\\D\u00F6\x05A\u00B0\u00BD\u00D6Cz\u008C1\u00F7\u00D7\u00A6\x7F\x13\x04\u00C1\u00F1$\u00F9&iA\u00E9t\u00BA_U\u00BF\u009F:\u00B7\u00D6^\x10\u0091\u00B7Uu\u00A31\u00E6d\u00A1P\u00B8}.9\"\u00A2\"\u00F23pMD~I\u00A5R\u0093S\u00CD\u00CF\x18\u00B3.\u008E\u00E3uTozo\u00D2\x1A=\u00CFk\x0Eq\x1Cw\x02m\u00C0A\u00A0\x02\u008C\u00A9\u00EA\u008Ba\x18\u00DE\x13EQ\u00BF\u0088\x1CQ\u00D5=\u00AA\u00BA'\u008E\u00E3kI\u00F3\x137@\u00E0\u00A5\u0096\u0096\u0096\u00B3\u00F5\x03\u00CE\u00B9\u00B6\u00DA\u00E1\n\u00E0\u00CE\u00B9\x06Yk\u00BF\x12\u0091\u00AD\"\u00D2\u00EF\u009C;\x04\u00AC1\u00C6\u00F4d2\u0099\u00EFZZZ\u00FA\u0080y\"2\u00D4@\u008D\u009E\u00E75\x01\u00E7\u00DC\u00B2\u00DA\u00E1\x1A\u00E0M\u00A0\x1B\u00F8@U\u009Fu\u00CE\u00AD\x03~\x02\u008E\x034\u00F2X\u009C\u00A8\x01\u0086ax\x17\u00B0\x14\x18\u00AB\x1B[/\"\u00BBk\u00A7\u009AJ\u00A5\x0E\u0085a\u00F8\u00E5\\W\u0082\u00E9t\u00FA\u00A4s\u00EE(\u00D5\x1B\u00C49\u00B7?\f\u00C3\u00FD\u00C06\u00A07\b\u0082\u00E1$5z\u009E\u00D7<\u008C1W\u00EANW\x03_\u00A8\u00EA\x1D@,\"K\u0080\u00FB\u0080\r\r\u00E7'\u0099\x1C\u00C7\u00B1\x02\u00A8\u00EAc\x00\u0083\u0083\u0083[Dd\u00A7\u00AA~\"\"+\u0081\u00D7T\u00F5I\u00AA+\u00B9\x1D\u00FF\u0096\x17EQk\u00B9\\\u008E\u0080\u00B5u\u00C3\u008F\x03[Tu\u00A3\u00B5\u00B6\u0090\u00A4>\u00CF\u00F3\u009AK\x1C\u00C7'\x00\u00AD\x1B\u009A'\"\u009BE\u00E4#\u00E0\u0089\x1B\u00CDO\u00D4\x00s\u00B9\u00DC\u00AF\u00C0%U\u00DD\x1EE\u00D1\u00D3\"r@U\u0097\x03\u00EF\x06A\u00F05\u00F0[\u00AD\u00D8\u00C2\u00D8\u00D8\u00D8\u00AE\u00D9\u00B2FGG\u00DB\u009Cs#@\u00C7u\u0097&\u0081\u00DEE\u008B\x16%\u00FA3\u00D3\u00F3\u00BC\u00E6\u0093\u00CB\u00E5.\x02\u00C59L\u00D5\x7F\u009F\u00F2O\u0089\x1A\u00A0\u0088(\u00F01\u00D5\u00A6\u00F5\x02\u00D5g\u00F2\u00CEl6\u00FB-\u0080\u00B5\u00F6\u00B0\u00AA>W*\u0095z\u00FB\u00FA\u00FA&g\u00CA\u0099a\u00E5\x0709\u00B5\u00F2\x1B\x1F\x1F\x7F\u00A3X,>\u0093\u00A4>\u00CF\u00F3\u009AO*\u0095\u00DAJuG\u00C8l.7\u0092\u009D\u00F8%H*\u0095\u00CA\x03\u00E7\u009Ds;\u0080\u00A7T\u00F5\u00CF\u00FA\u00EB\u00D9lvd\u00B6\u00E67000o\u00B6\u0095_6\u009B-\u0086a\u00B8\x0B\u00D8i\u008C)\u0086a\u00B8>i\u008D\u009E\u00E75\u008F\u00AE\u00AE\u00AE\x12\u00D0I\u00DD\u00BB\u0087i\x1Ch$;\u0095d\u00F2\u00E0\u00E0`_\u00A5R\u00D9'\"[\u009CsVD\u00BAE\u00A4+\f\u00C3\u008B\u00C0\x0F\u00B5i\x07\u00AD\u00B5Gf\u00CA\u0098?\x7F\u00FE2U]u\u00DD\u00F0$\u00D0;\u00F5\u009F\u00DF\u00C2\u0085\x0B\u00DF\x19\x1F\x1F_.\"\u00A7\u0083 \u00F8,I\u008D\u009E\u00E75\u0087\u0091\u0091\u0091\u0085\u0095J\u00E5U\u00E7\u00DCZ`\x15\u00D5\u00ED0\u00D3\x19(\u0095J\u00BBg\u00B86\u00AB9o\u0084.\x16\u008B\u00B7\x19c\u00CES\u00FDB\u00A3\u00D3Z{\u00AEP(<j\u008C\u00B1\"\u00B2RDV\u00ABj;0\u00E6\u009C{d\u00B6\r\u00D1CCC=\u00AAz\u0098j\x03\u00FE[\u00F3\u009B\u0092\u00CF\u00E7\u00EFmoo/uttT\x1A\u00B91\u00CF\u00F3\u00FE\u00DB\u0086\u0087\u0087\x17\u00C7q|\x16x\u00B0n\u00D8\x01\x07U\u00F5\u008A\u0088\\u\u00CE\u009D\u00B9\u0091\u00AFB\x12}\t2<<\u00BC<\u008E\u00E3[\u00AC\u00B5\u00E7\u00A6+\u00B6R\u00A9\u00BC%\"+\u008C1\u009B2\u0099\u00CC\u008F\u00B3e\u00D5\u009A\u00E0{\u00C06\u00FF\u00B6\u00D7\u00F3\u00BC\u00E9DQ\u00B4\u00C49\u00D7\x0Fd\u0080\u00C5@\u00DF\u00CD\u00EC\x17\x7F\x01\u00D0\u00BB\u00C9\u0082\u0095bR\u00F1\x00\x00\x00\x00IEND\u00AEB`\u0082", "w320_progress.png", "w320" );
w320_progress;

/**
 * The list of available icons.
 * @enum {DuBinary}
 * @static
 * @memberof DuScriptUI
 */
DuScriptUI.Icon = {
    ADD: w12_add,
    AE_BLUE: w8_ae_blue,
    AE_ORANGE: w8_ae_orange,
    BACK: w12_back,
    BOX_CHECKED: w12_box_checked,
    BOX_UNCHECKED: w12_box_unchecked,
    BUG: w16_bug,
    BUG_REPORT: w12_bugreport,
    CHECK: w12_check,
    CLOSE: w12_close,
    DOWNLOAD: w16_download,
    EDIT: w12_edit,
    EXPERT: w16_expert,
    EYE_DROPPER: w12_eye_dropper,
    EYE_DROPPER_BIG: w16_eye_dropper,
    FEATURE: w12_feature,
    FILE: w12_file,
    FOLDER: w12_folder,
    FOLDER_CLOSED: w12_folder_closed,
    GO_TO: w12_goto,
    HEART: w12_heart,
    HELP: w12_help,
    LANGUAGE: w16_language,
    LANGUAGE_FILE: w16_language_file,
    LANGUAGE_SMALL: w12_language,
    LIST: w16_list,
    MENU: w12_menu,
    MORE: w12_more,
    NEXT: w12_next,
    OPTIONS: w12_options,
    PARENT: w12_parent,
    PIN: w12_pin,
    PINNED: w12_pinned,
    PLACEHOLDER: w16_placeholder,
    PROGRESS: w320_progress,
    RAINBOX_RED: w8_rx_red,
    RANDOM: w16_random,
    REMOVE: w12_remove,
    RESET: w12_reset,
    ROOKIE: w16_rookie,
    RUN: w12_run,
    RX_PURPLE: w8_rx_purple,
    SETTINGS: w12_settings,
    SETTINGS_FILE: w16_settings_file,
    STANDARD: w16_standard,
    UPDATE: w12_update,
    USER: w16_user,
    SEARCH: w12_search,
    SORT: w12_sort,
    SORT_UP: w12_sort_up,
    SORT_DOWN: w12_sort_down,
};

// ==================== |----------| ====================
// ==================== | titleBar | ====================
// ==================== |----------| ====================

/**
 * @class
 * @name DuTitleBar
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A title bar.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.titleBar} to create  title bar.<br />
 * The itle bar inherits the <code>Group</code> object from ScriptUI and has all of its properties and methods.
 * @property {Boolean} pinned True when the title bar has been pinned and the corresponding panel should not be hidden.
 * @property {DuTitleBar~onClose} onClose - The function to execute when the close button is clicked
 * @property {DuTitleBar~onPin} onPin - The function to execute when the pin button is clicked
 * @category DuScriptUI
 */

/**
 * The function to execute when the close button is clicked
 * @callback DuTitleBar~onClose
 * @memberof DuTitleBar
 */

/**
 * The function to execute when the pin button is clicked
 * @callback DuTitleBar~onPin
 * @param {Boolean} pinned Wether the bar has been pin or not
 * @memberof DuTitleBar
 */

/**
 * Creates a titlebar
 * @param {Panel|Window|Group} container The ScriptUI Object which will contain and display the titlebar.
 * @param {string} [title=""] The title.
 * @param {Boolean} [closeButton=true] Wether to add a close button
 * @param {Boolean} [pinButton=true] Wether to add a pin button
 * @returns {DuTitleBar} The titlebar
 */
DuScriptUI.titleBar = function( container, title, closeButton, pinButton ) {
    // Defaults
    title = def(title, "");
    closeButton = def(closeButton, true);
    pinButton = def(pinButton, true);

    // Creates the title bar
    var titleBar = container.add( 'group' );
    titleBar.margins = 0;
    titleBar.spacing = 0;
    titleBar.orientation = 'row';
    titleBar.alignment = ['fill','top'];
    DuScriptUI.setBackgroundColor( titleBar, DuColor.Color.DARK_GREY );

    titleBar.onClose = function() {};
    titleBar.onPin = function(p) {};

    // Pin button
    if(pinButton) {
        titleBar.pinButton = DuScriptUI.checkBox(titleBar, '', w12_pin, i18n._("Keep this panel open"), '', w12_pinned);
        titleBar.pinButton.alignment = [ 'left', 'center' ];
        titleBar.pinButton.onClick = function() {
            titleBar.pinned = titleBar.pinButton.checked;
            titleBar.onPin(titleBar.pinned);
        };
    }

    // Title
    if (title != "") {
        titleBar.titleLabel = titleBar.add('statictext', undefined,  title);
        titleBar.titleLabel.alignment = [ 'center', 'center' ];
    }

    // Close
    if (closeButton) {
        titleBar.closeButton = DuScriptUI.button(titleBar, '', w12_close, 'Close');
        titleBar.closeButton.alignment = [ 'right', 'center' ];
        titleBar.closeButton.onClick = function() {
            titleBar.onClose();
        };
    }

    return titleBar;
}// ==================== |-------| ====================
// ==================== | panel | ====================
// ==================== |-------| ====================


/**
 * @class
 * @name DuPanel
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A Panel or Window.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.mainPanel} to create a Panel.<br />
 * The DuPanel inherits the Panel or Window object from ScriptUI and has all of its properties and methods.
 * @category DuScriptUI
 */

/**
 * The ScriptUI group where the new controls must be added. Do not add any control directly inside the DuPanel.
 * @name content
 * @type {Group}
 * @memberof DuPanel.prototype
 */

/**
 * A function to reload a script in this panel.
 * @method refreshUi
 * @param {File} file - The script to reload. 
 * @return {boolean} true on success, false otherwise.
 * @memberof DuPanel.prototype
 */

/**
 * Creates the main panel for a script
 * @param {Panel|null} container - The container ('this' in the root of the calling script), either a Panel (when launched from the 'Window' menu) or null (when launched from 'file/scripts/run...')
 * @param {string} [scriptName=DuESF.scriptName] - A name for this UI
 * @param {string[]} [contentAlignment=DuScriptUI.defaultColumnAlignment] - The alignment of the content in the panel
 * @param {string} [borderless=false] - When true, creates a borderless window if container is not a panel
 * @return {DuPanel} The panel created, either a ScriptUI Panel or a ScriptUI Window.
 * @example
 * var ui = DuScriptUI.mainPanel(this,"Test Script");
 * var refreshButton = ui.content.add('button',undefined,"Refresh");
 * refreshButton.onClick = function() { ui.refreshUI( new File($.fileName) ); }; //reloads the current script
 * DuScriptUI.showUI(ui);
 */
DuScriptUI.mainPanel = function( container, scriptName, contentAlignment, bless )
{
    scriptName = def(scriptName, DuESF.scriptName, false);
    contentAlignment = def(contentAlignment, DuScriptUI.defaultColumnAlignment, false);
    bless = def (bless, false);

    var myPal = null;

    var winType = 'palette';

    if (DuESF.host == DuESF.HostApplication.PHOTOSHOP)
    {
        bless = false;
        winType = 'dialog';
    }

    container instanceof Panel ? myPal = container : myPal = new Window( winType, scriptName, undefined,
    {
        resizeable: true,
        borderless: bless
    } );

    if ( myPal == null ) throw "Failed to create User Interface.";

    myPal.margins = 0;
    myPal.spacing = 0;

    //create margins to dim buttons
    var topMargin = myPal.add( 'button' );
    topMargin.onDraw = function() {};
    topMargin.minimumSize.height = DuScriptUI.defaultMargins;
    topMargin.alignment = [ 'fill', 'top' ];
    topMargin.maximumSize.height = topMargin.minimumSize.height = topMargin.preferredSize.height = DuScriptUI.defaultMargins;

    var mainRow = myPal.add( 'group' );
    mainRow.alignment = contentAlignment;
    mainRow.alignChildren = contentAlignment;
    mainRow.margins = 0;
    mainRow.spacing = 0;

    var leftMargin = mainRow.add( 'button' );
    leftMargin.onDraw = function() {};
    leftMargin.maximumSize.width = leftMargin.minimumSize.width = leftMargin.preferredSize.width = DuScriptUI.defaultMargins;
    leftMargin.alignment = [ 'left', 'fill' ];

    myPal.content = mainRow.add( 'group' );
    myPal.content.margins = 0;
    myPal.content.spacing = DuScriptUI.defaultSpacing;
    myPal.content.alignChildren = DuScriptUI.defaultColumnAlignment;
    myPal.content.orientation = "column";
    myPal.content.alignment = ['fill', 'fill'];

    var rightMargin = mainRow.add( 'button' );
    rightMargin.onDraw = function() {};
    rightMargin.maximumSize.width = rightMargin.minimumSize.width = rightMargin.preferredSize.width = DuScriptUI.defaultMargins;
    rightMargin.alignment = [ 'right', 'fill' ];

    var bottomMargin = myPal.add( 'button' );
    bottomMargin.onDraw = function() {};
    bottomMargin.maximumSize.height = bottomMargin.minimumSize.height = bottomMargin.preferredSize.height = DuScriptUI.defaultMargins;
    bottomMargin.alignment = [ 'fill', 'bottom' ];

    myPal.addEventListener( "mouseover", DuScriptUI.dimControls );
    topMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    leftMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    rightMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    bottomMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
//*/
    return myPal;
}

/**
 * @class
 * @name DuPopup
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A borderless popup, to be tied to a ui control.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.popUp} to create a Popup.<br />
 * The DuPopup inherits the Window object from ScriptUI and has all of its properties and methods.
 * @property {Group} content - The ScriptUI group where the new controls must be added. Do not add any control directly inside the DuPopup.
 * @property {boolean} pinned - true if this popup is "pinned", which means it won't hide when the user clicks outside of the window.
 * @property {DuPopup~build} build - You can use this callback to add a function which builds the UI of the popup, it will be called on first display.
 * @category DuScriptUI
 */

/**
 * You can use this callback to add a function which builds the UI of the popup, it will be called on first display.<br />
 * This allows a faster startup of your script by delaying the creation of the tabs which are hidden at startup.<br />
 * You can add controls in the <code>this.content</code> object.
 * @callback DuPopup~build
 * @memberof DuPopup
 */
/**
 * Ties the popup to a ui control. The popup will be shown just above the control when it is clicked.<br />
 * The control must have an addEventListener method.
 * @method
 * @memberof DuPopup
 * @name tieTo
 * @param {ScriptUI} [control] - The control
 * @param {Boolean} [onShift=false] - If set to true, the popup is tied on Shift + Click only
 * @param {boolean} [alwaysBlock=false] If true, the popup will never be automatically shown. Call show() to show it.
 */

/**
 * Pins the popup (it won't be hidden anymore when deactivated).
 * @method
 * @memberof DuPopup
 * @name pin
 * @param {boolean} [pinned=true] - true to pin the popup, false to un-pin it.
 */

/**
 * Hides the popup.
 * @method
 * @memberof DuPopup
 * @name hidePopup
 */

/**
 * Hides the popup (alias for {@link DuPopup.hidePopup}).
 * @alias DuPopup.hidePopup
 * @method
 * @memberof DuPopup
 * @name cancel
 */

/**
 * Sets this parameter to <code>true</code> to prevent the next show of the popup.<br />
 * This will prevent it from showing once (and only once).
 * @type {Boolean}
 * @memberof DuPopup
 * @name block
 * @default false
 */

/**
 * Creates a borderless popup
 * @param {string} title The title of the popup
 * @param {string[]} [alignment=[ "fill", "top" ]] The alignement of this window
 * @param {boolean} [modal=false] Set the popup to a modal dialog
 * @return {DuPopup} The popup, a ScriptUI Window which is borderless, with a 'tieTo(control)' method.
 * @example
 * var popup = DuScriptUI.popUp( );
 * var popupButton = DuScriptUI.button( myUI, "My Buttton for the popup" );
 * popup.tieTo( popupButton ); // will show the popup when the button is clicked, just above it.
 */
DuScriptUI.popUp = function( title, alignment, modal )
{
    modal = def(modal, false);
    alignment = def( alignment, [ "fill", "top" ] );
    title = def (title, "");
    
    var type = 'palette';
    if (modal) type = 'dialog'; 
    var popup = new Window( type, title, undefined,
    {
        resizeable: false,
        borderless: true
    } );

    DuScriptUI.setBackgroundColor(popup, DuColor.Color.OBSIDIAN);

    popup.margins = 0;
    popup.spacing = 0;
    popup.pinned = false;

    //add stroke around
    function drawBorder ( drawState ) {
        /*var g = this.graphics;
        var c = DuColor.Color.DARK_GREY;

        var pen = g.newPen( g.PenType.SOLID_COLOR, c, 1 );

        g.newPath();
        if (this.side == 0) {
            g.moveTo (0, 0);
            g.lineTo (this.size.width, 0);
        }
        else if (this.side == 2) {
            g.moveTo (this.size.width, 0);
            g.lineTo (this.size.width, this.size.height);
        }
        else if (this.side == 1) {
            g.moveTo (0, 0);
            g.lineTo (0, this.size.height);
        }
        else if (this.side == 3) {
            g.moveTo (0, this.size.height);
            g.lineTo (this.size.width, this.size.height);
        }

        g.strokePath( pen );*/
    };

    //create margins to dim buttons
    var topMargin = popup.add( 'button' );
    topMargin.onDraw = drawBorder;
    topMargin.side = 0;
    topMargin.minimumSize.height = DuScriptUI.defaultMargins;
    topMargin.alignment = [ 'fill', 'top' ];
    topMargin.maximumSize.height = topMargin.minimumSize.height = topMargin.preferredSize.height = 1;
    
    var mainRow = popup.add( 'group' );
    mainRow.alignment = alignment;
    mainRow.alignChildren = alignment;
    mainRow.margins = 0;
    mainRow.spacing = 0;

    var leftMargin = mainRow.add( 'button' );
    leftMargin.onDraw = drawBorder;
    leftMargin.side = 1;
    leftMargin.maximumSize.width = leftMargin.minimumSize.width = leftMargin.preferredSize.width = 1;
    leftMargin.alignment = [ 'left', 'fill' ];

    var mainColumn = mainRow.add( 'group' );
    mainColumn.orientation = 'column';
    mainColumn.spacing = 0;
    mainColumn.margins = 0;
    mainColumn.alignment = ['fill','fill'];

    var titleBar = DuScriptUI.titleBar(
        mainColumn,
        title
        );
    if (modal) DuScriptUI.setTextColor(titleBar.titleLabel, DuColor.Color.APP_HIGHLIGHT_COLOR)

    popup.content = mainColumn.add( 'group' );
    popup.content.margins = 0;
    popup.content.spacing = 0;
    popup.content.alignChildren = alignment;
    popup.content.orientation = "column";

    var rightMargin = mainRow.add( 'button' );
    rightMargin.onDraw = drawBorder;
    rightMargin.side = 2;
    rightMargin.maximumSize.width = rightMargin.minimumSize.width = rightMargin.preferredSize.width = 1;
    rightMargin.alignment = [ 'right', 'fill' ];

    var bottomMargin = popup.add( 'button' );
    bottomMargin.onDraw = drawBorder;
    bottomMargin.side = 3;
    bottomMargin.maximumSize.height = bottomMargin.minimumSize.height = bottomMargin.preferredSize.height = 1;
    bottomMargin.alignment = [ 'fill', 'bottom' ];

    popup.block = false;

    popup.build = function( contentGroup ) {};
    popup.built = false;

    popup.tiedAlwaysBlock = false;

    popup.tieTo = function (control, onShift, alwaysBlock)
    {
        onShift = def(onShift, false);
        alwaysBlock = def(alwaysBlock, false);

        popup.tiedAlwaysBlock = alwaysBlock;

        control.tiedPopups = def(control.tiedPopups, []);
        control.tiedOnShift  =def(control.tiedOnShift, []);
        control.tiedPopups.push(popup);
        control.tiedOnShift.push(onShift);
        
        if (control.tiedPopups.length == 1)
        {
            control.addEventListener( 'click', function(e) {
                if(e.view.parent != control) return;

                for (var i = 0; i < control.tiedPopups.length; i++)
                {
                    var ppup = control.tiedPopups[i];
                    if ( !e.shiftKey && control.tiedOnShift[i] ) return;

                    if (ppup.block)
                    {
                        ppup.block = false;
                        return;
                    }

                    if ( !ppup.built )
                    {
                        ppup.build( );
                        ppup.built = true;
                    }

                    var x = e.screenX - e.clientX;
                    var y = e.screenY - e.clientY;
                    //we need a layout and resize to get the window frame size
                    DuScriptUI.layout( ppup );
                    var location = [x,y];
                    location = DuScriptUI.moveInsideScreen( [x, y], ppup.frameSize );
                    ppup.location = location;
                    if (!ppup.pinned) DuScriptUI.eventFunctionsPaused = true;
                    if (!ppup.tiedAlwaysBlock) ppup.show();
                }
            }, true);
        }
    }

    popup.pin = function(p)
    {
        p = def (p, true);
        popup.pinned = p;
        titleBar.pinButton.setChecked(p);
    }

    var movingTitle = false;
    var movingPopup = false;
    var startMouseLocation = [0,0];
    var startPopupLocation = [0,0];

    function enterTitleMoveState(e)
    {
        movingTitle = true;
        startMouseLocation = [e.screenX, e.screenY];
        startPopupLocation = [popup.location[0], popup.location[1]];
    }

    function enterPopupMoveState(e)
    {
        movingPopup = true;
        startMouseLocation = [e.screenX, e.screenY];
        startPopupLocation = [popup.location[0], popup.location[1]];
    }

    function movePopup(e)
    {
        if (!movingPopup) return;
        if (movingTitle) return;
        var x = e.screenX - startMouseLocation[0];
        var y = e.screenY - startMouseLocation[1];
        popup.location = [ x, y ] + startPopupLocation;
    }

    function moveTitle(e)
    {
        if (!movingTitle) return;
        var x = e.screenX - startMouseLocation[0];
        var y = e.screenY - startMouseLocation[1];
        popup.location = [ x, y ] + startPopupLocation;
    }

    function endMoveState(e)
    {
        movingTitle = false;
        movingPopup = false;
    }

    function esc(e)
    {
        if ( e.keyName == "Escape") { hidePopup(); }
    }

    function hidePopup()
    {
        popup.hide();
        DuScriptUI.eventFunctionsPaused = false;
    }

    popup.cancel = popup.hidePopup = hidePopup;

    popup.onDeactivate = function () { if (!popup.pinned) hidePopup(); };
    titleBar.onClose = hidePopup;

    titleBar.onPin = function (pinned) { popup.pin(pinned); };

    popup.addEventListener( "mouseover", DuScriptUI.dimControls );
    popup.addEventListener( "keydown", esc );
    topMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    leftMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    rightMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    bottomMargin.addEventListener( "mouseover", DuScriptUI.dimControls );
    titleBar.addEventListener( "mouseover", DuScriptUI.dimControls );

    titleBar.addEventListener( "mousedown", enterPopupMoveState );
    titleBar.addEventListener( "mousemove", movePopup);
    titleBar.addEventListener( "mouseup", endMoveState );
    titleBar.addEventListener( "mouseout", endMoveState );

    titleBar.titleLabel.addEventListener( "mousedown", enterTitleMoveState );
    titleBar.titleLabel.addEventListener( "mousemove", moveTitle );
    titleBar.titleLabel.addEventListener( "mouseup", endMoveState );
    titleBar.titleLabel.addEventListener( "mouseout", endMoveState );

    if (modal) popup.pin();

    return popup;
}

/**
 * Resizes and shows the main panel of a script
 * @param {Panel|Window}		ui	- The UI created by Duik.ui.createUI
 * @param {boolean}		[enterRunTime=false]	- Set to true to automatically set DuESF to runtime state<br />
 * Set this to false if the ui shown is not the actual main panel of the script and it is shown before the main panel has been loaded.
 */
DuScriptUI.showUI = function( ui, enterRunTime )
{
    DuScriptUI.updateFundingInfo();

    enterRunTime = def( enterRunTime, true);

    DuDebug.log( "DuScriptUI.showUI" );

    DuDebug.log( "DuScriptUI.showUI: Defining layout & resize" );
    DuScriptUI.layout( ui, true );
    DuDebug.log( "DuScriptUI.showUI: Layout OK" );
    ui.onResizing = ui.onResize = function()
    {
        try
        {
            ui.layout.resize();
        }
        catch ( e )
        {}
    }
    DuDebug.log( "DuScriptUI.showUI: UI Ready" );

    // If it's a Window, it needs to be shown
    if ( ui instanceof Window )
    {
        DuDebug.log( "DuScriptUI.showUI: Showing window" );
        //ui.center();
        ui.show();
        DuDebug.log( "DuScriptUI.showUI: Window visible." );
    }
    DuDebug.log( "DuScriptUI.showUI: UI shown" );

    DuScriptUI.uiShown = true;
    
    if (enterRunTime) DuESF.enterRunTime();
    DuDebug.log( "DuScriptUI.showUI: Runtime!" );

    // Build tabs
    for (var i = 0, n = DuScriptUI.allTabs.length; i < n; i++)
    {
        var tab = DuScriptUI.allTabs[i];
        if ( !tab.activated ) continue;
        tab.duBuild();
        tab.visible = true;
        tab.tabActivated();
    }//*/
    DuDebug.log( "DuScriptUI.showUI: Tabs built." );
}

/**
 * @class
 * @name DuScriptPanel
 * @extends DuPanel
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A Panel or Window.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.scriptPanel} to create a Panel.<br />
 * @property {Group} settingsGroup - The ScriptUI group where to add the UI for the settings of the script.
 * @property {Group} mainGroup - The ScriptUI group where to add the main UI of the script.
 * @property {DuScriptPanel~onApplySettings} onApplySettings Called when the apply settings button is clicked.
 * @property {DuScriptPanel~onResetSettings} onResetSettings Called when the reset (default) settings button is clicked.
 * @category DuScriptUI
 */

/**
 * The function to execute when the apply settings button is clicked.
 * @callback DuScriptPanel~onApplySettings
 * @memberof DuScriptPanel
 */

/**
 * The function to execute when the reset settings button is clicked.
 * @callback DuScriptPanel~onResetSettings
 * @memberof DuScriptPanel
 */

/**
 * Adds some settings common to all scripts (the file, highlight color, languages...)
 * @method addCommonSettings
 * @memberof DuScriptPanel.prototype
 */

/**
 * Creates the main panel of a script
 * @param {Panel|null} container - The container ('this' in the root of the calling script), either a Panel (when launched from the 'Window' menu) or null (when launched from 'file/scripts/run...')
 * @param {bool} [addSettingsButton=true] - Whether to create a button to open the settings or not
 * @param {bool} [addHelpButton=false] - Whether to create a button to open the help panel or not
 * @param {File} [scriptFile] - The main script file, needed for the refresh button in debug mode
 * @return {DuScriptPanel} The panel created, either a ScriptUI Panel or a ScriptUI Window.
 * @example
 * var ui = DuScriptUI.mainPanel(this,"Test Script");
 * var refreshButton = ui.content.add('button',undefined,"Refresh");
 * refreshButton.onClick = function() { ui.refreshUI( new File($.fileName) ); }; //reloads the current script
 * DuScriptUI.showUI(ui);
 */
DuScriptUI.scriptPanel = function( container, addSettingsButton, addHelpButton, scriptFile )
{
    function testGroup( group )
    {
        group.add('statictext', undefined, "test");
        DuScriptUI.setBackgroundColor( group, DuColor.Color.BLACK );
    }

    addSettingsButton = def(addSettingsButton, true);
    addHelpButton = def(addHelpButton, false);

    var panel = DuScriptUI.mainPanel( container, undefined, ['fill','fill'] );
    panel.content.orientation = 'stack';
    panel.content.alignment = ['fill','fill'];

    var contentsMainGroup = DuScriptUI.group(panel.content, 'column');
    contentsMainGroup.alignment = ['fill', 'fill'];
    contentsMainGroup.margins = 0;

    var contentsMainStack = DuScriptUI.group(contentsMainGroup, 'column');
    contentsMainStack.orientation = 'stack';
    contentsMainStack.alignment = ['fill', 'fill'];

    panel.mainGroup = DuScriptUI.group(contentsMainStack, 'column');
    panel.mainGroup.alignment = ['fill', 'fill'];

    //a margin at the bottom (hidden by the bottom bar buttons)
    var mainGroupMarginBottom = DuScriptUI.group(contentsMainGroup, 'column');
    mainGroupMarginBottom.alignment= ['fill', 'bottom'];
    //mainGroupMarginBottom.minimumSize = [0, 16];

    // == SETTINGS PANEL
    var rootSettingsGroup = DuScriptUI.group(contentsMainStack, 'column');
    rootSettingsGroup.alignment = ['fill', 'fill'];
    rootSettingsGroup.visible = false;
    panel.settingsGroup = DuScriptUI.group(rootSettingsGroup, 'column');
    panel.settingsGroup.alignment = ['fill', 'top'];
    var settingsTitle = DuScriptUI.titleBar(
        panel.settingsGroup,
        i18n._( "%1 Settings", DuESF.scriptName),
        true,
        false
    );

    panel.hasCommonSettings = false;
    panel.addCommonSettings = function()
    {
        DuDebug.log("ScriptUI: adding settings to main panel.");
        if (panel.hasCommonSettings) return;
        panel.hasCommonSettings = true;

        var debug = DuESF.scriptSettings.get("common/debug", false);

        // Add default settings
        // language
        // Prepare languages, count how many there are, etc
        var languages = i18n.getAvailableLanguages();

        var languageGroup = DuScriptUI.group(panel.settingsGroup, 'row');

        if (languages.count >= 1)
        {
            panel.settingsGroup.languageSelector = DuScriptUI.selector(
                languageGroup,
                i18n._("Set the language of the interface.")
            )

            for (var locale in languages)
            {
                if (locale == "count") continue;

                DuDebug.log("ScriptUI: Found locale: " + locale);

                panel.settingsGroup.languageSelector.addButton( {
                    text: languages[locale],
                    image: w16_language,
                    data: locale
                }); 
            }

            panel.settingsGroup.languageSelector.setCurrentIndex(0);
        }
        DuDebug.log("ScriptUI: language settings ready.");
        
        // settings file
        panel.settingsGroup.fileButton = DuScriptUI.fileSelector (
            panel.settingsGroup,
            i18n._("Settings file") + '...',
            true,
            i18n._("Set the location of the settings file."),
            w16_settings_file,
            'save',
            "JSON: *.json, All Files: *.*",
            'column'
        );
        DuDebug.log("ScriptUI: settings file ready.");

        // color
        panel.settingsGroup.colorSelector = DuScriptUI.selector(
            panel.settingsGroup,
            i18n._("Set the highlight color.")
        );
        panel.settingsGroup.colorSelector.addButton(
            i18n._("After Effects Blue"),
            w8_ae_blue,
            i18n._("The After Effects highlighting blue")
        );
        panel.settingsGroup.colorSelector.addButton(
            i18n._("RxLab Purple"),
            w8_rx_purple,
            i18n._("The RxLaboratory Purple")
        );
        panel.settingsGroup.colorSelector.addButton(
            i18n._("Rainbox Red"),
            w8_rx_red,
            i18n._("The Rainbox Productions Red")
        );
        panel.settingsGroup.colorSelector.addButton(
            i18n._("After Effects Orange (CS6)"),
            w8_ae_orange,
            i18n._("The After Effects highlighting orange from good ol'CS6")
        );
        panel.settingsGroup.colorSelector.addButton(
            i18n._("Custom..."),
            undefined,
            i18n._("Select a custom color.")
        );
        panel.settingsGroup.colorSelector.setCurrentIndex(0);
        DuDebug.log("ScriptUI: color ready.");
        // ui mode
        panel.settingsGroup.uiModeSelector = DuScriptUI.selector(
            panel.settingsGroup,
            i18n._("Select the UI mode.")
        )
        panel.settingsGroup.uiModeSelector.addButton(
            i18n._("Rookie"),
            w16_rookie,
            i18n._("The easiest-to-use mode, but also the biggest UI.")
        );
        panel.settingsGroup.uiModeSelector.addButton(
            i18n._("Standard"),
            w16_standard,
            i18n._("The standard not-too-big UI.")
        );
        panel.settingsGroup.uiModeSelector.addButton(
            i18n._("Expert"),
            w16_expert,
            i18n._("The smallest UI, for expert users.")
        );
        panel.settingsGroup.uiModeSelector.setCurrentIndex(0);
        DuDebug.log("ScriptUI: ui mode ready.");
        // dev & debug
        panel.settingsGroup.debugButton = DuScriptUI.checkBox (
            panel.settingsGroup,
            i18n._("Normal mode"),
            w16_user,
            i18n._("Use at your own risk!"),
            i18n._("Dev and Debug mode"),
            w16_bug
        );
        DuDebug.log("ScriptUI: Dev and debug mode ready.");

        panel.settingsGroup.fileButton.onChange = function()
        {
            var file = panel.settingsGroup.fileButton.getFile();
            if (file == null) return;
            DuESF.scriptSettings.setFile(file);
            panel.settingsGroup.reset();
        }

        panel.settingsGroup.colorSelector.onChange = function()
        {
            var customColor = new DuColor( DuESF.scriptSettings.get("common/highlightColor", DuColor.Color.APP_HIGHLIGHT_COLOR) );
            var colorIndex = panel.settingsGroup.colorSelector.index;
            if (colorIndex == panel.settingsGroup.colorSelector.items.length -1 )
            {
                var newColor = new DuColor( colorPicker( customColor.floatRGB() ) );
                // If white, the user has canceled
                if ( newColor.equals( DuColor.Color.WHITE ) )
                {
                    panel.settingsGroup.colorSelector.setCurrentIndex(0);
                    return;
                }

                // Store the color for the valid button
                panel.settingsGroup.colorSelector.customColor = newColor;
            }
        }

        DuDebug.log("ScriptUI: events ready.");

        // Load settings
        panel.settingsGroup.reset = function()
        {
            var highlightSelection = DuESF.scriptSettings.get("common/highlightSelection", 0);
            var currentLanguageName = DuESF.scriptSettings.get("common/currentLanguageName", 'Esperanto');
            var debug = DuESF.scriptSettings.get("common/debug", false);
            var uiMode = DuESF.scriptSettings.get("common/uiMode", 0);
            var customColor = new DuColor( DuESF.scriptSettings.get("common/highlightColor", DuColor.Color.APP_HIGHLIGHT_COLOR) );
            
            // Settings File
            panel.settingsGroup.fileButton.setPath( DuESF.scriptSettings.file.absoluteURI );

            // Ui mode
            panel.settingsGroup.uiModeSelector.setCurrentIndex( uiMode );

            // Color
            panel.settingsGroup.colorSelector.setCurrentIndex( highlightSelection, true );
            // Set custom
            panel.settingsGroup.colorSelector.customColor = customColor;

            // Language
            if (panel.settingsGroup.languageSelector)
                for (var i = 0 , num = panel.settingsGroup.languageSelector.items.length; i < num; i++)
                {
                    if (panel.settingsGroup.languageSelector.items[i][0] == currentLanguageName)
                    {
                        panel.settingsGroup.languageSelector.setCurrentIndex(i);
                        break;
                    }
                }

            // Debug mode
            panel.settingsGroup.debugButton.setChecked( debug );

            panel.onResetSettings();
        }

        DuDebug.log("ScriptUI: calling reset method...");

        panel.settingsGroup.reset();

        DuDebug.log("ScriptUI: settings panel ready!");
    }

    DuScriptUI.separator( rootSettingsGroup );

    panel.settingsGroup.updateButton = DuScriptUI.button(
        rootSettingsGroup,
        i18n._("Check for updates"),
        DuScriptUI.Icon.DOWNLOAD,
        i18n._("Check for updates"),
        false,
        'row',
        'center'
    )

    DuScriptUI.separator( rootSettingsGroup );

    var validGroup = DuScriptUI.group( rootSettingsGroup, 'row');

    panel.settingsGroup.resetButton = DuScriptUI.button(
        validGroup,
        i18n._("Default"),
        w12_reset,
        i18n._("Reset the settings to their default values."),
        false,
        'row',
        'center'
    );
    panel.settingsGroup.validButton = DuScriptUI.button(
        validGroup,
        i18n._("Apply"),
        w12_check,
        i18n._("Apply settings."),
        false,
        'row',
        'center'
    );
    
    panel.onApplySettings = function() {};
    panel.onResetSettings = function() {};

    panel.settingsGroup.validButton.onClick = function()
    {
        if (!panel.hasCommonSettings)
        {
            panel.onApplySettings();
            return;
        }

        // Update and Save settings

        // UI Mode
        DuESF.scriptSettings.set("common/uiMode", panel.settingsGroup.uiModeSelector.index);

        // Color
        var colorIndex = panel.settingsGroup.colorSelector.index;
        DuESF.scriptSettings.set("common/highlightSelection", colorIndex);
        // Custom
        if (colorIndex == panel.settingsGroup.colorSelector.items.length -1 )
        {
            // Save and set
            var color = panel.settingsGroup.colorSelector.customColor;
            DuESF.scriptSettings.set("common/highlightColor",  color.floatRGBA());
            DuColor.Color.APP_HIGHLIGHT_COLOR = color;
        }
        else if (colorIndex == 0)
        {
            DuESF.scriptSettings.set("common/highlightColor",  DuColor.Color.AFTER_EFFECTS_BLUE);
            DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.AFTER_EFFECTS_BLUE;
        }
        else if (colorIndex == 1)
        {
            DuESF.scriptSettings.set("common/highlightColor",  DuColor.Color.RX_PURPLE);
            DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.RX_PURPLE;
        }
        else if (colorIndex == 2)
        {
            DuESF.scriptSettings.set("common/highlightColor",  DuColor.Color.RAINBOX_RED);
            DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.RAINBOX_RED;
        }
        else if (colorIndex == 3)
        {
            DuESF.scriptSettings.set("common/highlightColor",  DuColor.Color.AE_ORANGE);
            DuColor.Color.APP_HIGHLIGHT_COLOR = DuColor.Color.AE_ORANGE;
        }

        // Language
        if (panel.settingsGroup.languageSelector)
        {
            DuESF.scriptSettings.set("common/language",  panel.settingsGroup.languageSelector.currentData );
            DuESF.scriptSettings.set("common/currentLanguageName",  panel.settingsGroup.languageSelector.text);
        }
            
        // Dev Mode
        DuESF.scriptSettings.set("common/debug",  panel.settingsGroup.debugButton.value);
		DuESF.debug = panel.settingsGroup.debugButton.value;

        DuESF.scriptSettings.save();

        panel.mainGroup.visible = true;
        rootSettingsGroup.visible = false;

        panel.onApplySettings();

        // Restart script
        var ok = DuScriptUI.refreshPanel( panel, scriptFile );
        if (!ok) alert(i18n._("You may need to restart the script for all changes to take effect."));
    }

    panel.settingsGroup.resetButton.onClick = function()
    {
        DuESF.settings.reset();
        DuESF.scriptSettings.reset();
        panel.settingsGroup.reset();
    }

    panel.settingsGroup.updateButton.onClick = function()
    {
        DuScriptUI.checkUpdate(undefined, undefined, true);
    }

    // == Bottom Line
    var bottomUberGroup = mainGroupMarginBottom;
    // TODO Test in Ae
    /*var bottomUberGroup = DuScriptUI.group(panel.content, 'column');
    bottomUberGroup.alignment = ['fill', 'bottom'];
    bottomUberGroup.margins = 0;
    bottomUberGroup.spacing = 0;*/

    // Draw a thin line like other panels.
    //var bottomLine = DuScriptUI.separator( bottomUberGroup );
    if ( DuESF.rxVersionURL != '' ) {
        DuScriptUI.fundingBar = bottomUberGroup.add( 'progressbar' );
        DuScriptUI.fundingBar.maximumSize = [32000, 2];
        DuScriptUI.fundingBar.alignment = ['fill', 'top'];
        DuScriptUI.fundingBar.helpTip = i18n._("Thank you for your donations!");
        DuScriptUI.updateFundingInfo();
    }

    panel.bottomGroup = DuScriptUI.group(bottomUberGroup, 'row');
    panel.bottomGroup.margins = 0;

    if (DuESF.bugReportURL != '' || addSettingsButton || addHelpButton || DuESF.translateURL != '')
    {
        panel.moreButton = DuScriptUI.multiButton( panel.bottomGroup,
            {
                text: "",
                helpTip: i18n._("Help and options")
            }
        );
        panel.moreButton.alignment = ['left', 'center'];

        if (DuESF.bugReportURL != '')
        {
            var bugButton = DuScriptUI.addBugButton( panel.moreButton );
            bugButton.alignment = ['left', 'fill'];
        }
        
        if (addSettingsButton)
        {
            var settingsButton = panel.moreButton.addButton(
                i18n._("Edit settings"),
                w12_settings,
                i18n._("Edit settings")
                );
            settingsButton.alignment = ['left', 'fill'];

            settingsButton.onClick = function()
            {
                if (!rootSettingsGroup.visible)
                {
                    if(panel.hasCommonSettings) panel.settingsGroup.reset();
                    panel.mainGroup.visible = false;
                    rootSettingsGroup.visible = true;
                }
                else
                {
                    panel.mainGroup.visible = true;
                    rootSettingsGroup.visible = false;
                }
            }

            settingsTitle.onClose = function()
            {
                if( panel.hasCommonSettings ) panel.settingsGroup.reset();
                panel.mainGroup.visible = true;
                rootSettingsGroup.visible = false;
            }           
        }

        if (DuESF.translateURL != '')
        {
            var translateButton = DuScriptUI.addTranslateButton( panel.moreButton );
            translateButton.alignment = ['left', 'fill'];
        }

        if (addHelpButton)
        {
            var helpButton = DuScriptUI.addHelpButton( panel.moreButton, false );
            helpButton.alignment = ['left', 'fill'];
        }
    }

    var donateButton = DuScriptUI.addDonateButton( panel.bottomGroup, true);
    donateButton.alignment = ['left', 'fill'];

    var versionButton = DuScriptUI.versionButton(
        panel.bottomGroup
        );
    versionButton.alignment = ['right', 'bottom'];
    
    //Refresh button
    if ( DuESF.debug && (scriptFile instanceof File) && scriptFile.exists )
    {
        var refreshButton = panel.bottomGroup.add( 'button', undefined, 'R' );
        refreshButton.alignment = [ 'right', 'bottom' ];
        refreshButton.maximumSize = [ 20, 20 ];
        refreshButton.onClick = function()
        {
            DuScriptUI.refreshPanel( panel, scriptFile );
        };
    }

    return panel;
}

DuScriptUI.refreshPanel = function( panel, scriptFile )
{
    return DuScriptUI.refreshWindow( panel, scriptFile );
}

DuScriptUI.refreshWindow = function ( win, scriptFile )
{
    // If Panel is a window, close, and run script again
    if( win instanceof Window && scriptFile.exists)
    {
        win.close();
        $.evalFile( scriptFile );
        return true;
    }
    return false;
}

/**
 * Creates a popup to ask for a simple string
 * @param {string} title The title of the popup
 * @param {string} defaultString The placeholder for the edit text
 * @returns {DuPopup} The popup, with an <code>onAccept( str )</code> callback.
 */
DuScriptUI.stringPrompt = function ( title, defaultString )
{
    defaultString = def(defaultString, '');

    var nameEditor = DuScriptUI.popUp( title );
    nameEditor.content.alignment = ['fill','top'];
    
    nameEditor.editText = DuScriptUI.editText(
        nameEditor.content,
        '',
        '',
        '',
        defaultString
    )
    nameEditor.editText.alignment = ['fill','top'];

    var nameButtons = DuScriptUI.group( nameEditor.content );
    nameButtons.alignment = ['fill','top'];

    var nameOKButton = DuScriptUI.button(
        nameButtons,
        i18n._("OK"),
        DuScriptUI.Icon.CHECK,
        title,
        false,
        'row',
        'center'
    )

    nameEditor.previousString = '';

    nameEditor.onAccept = function( str ){};

    nameEditor.accept = function() {
        nameEditor.onAccept( nameEditor.editText.text );
        nameEditor.hidePopup();
        return nameEditor.editText.text;
    }

    nameEditor.setText = function (text) {
        nameEditor.previousString = text;
        nameEditor.editText.setText(text);
    }

    nameEditor.edit = function() {
        nameEditor.editText.clicked();
    }

    nameOKButton.onClick = nameEditor.accept;
    nameEditor.addEventListener('enterkey', nameEditor.accept );

    return nameEditor;
}// ==================== |--------| ====================
// ==================== | button | ====================
// ==================== |--------| ====================

/**
 * @class
 * @name DuButton
 * @classdesc For use with {@link DuScriptUI}.<br />
 * An Image Button.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.button} to create an Image Button.<br />
 * The Image Button inherits the <code>Group</code> object from ScriptUI and has all of its properties and methods.
 * @property {string} helpLink - A URL to a help page, shown on shift click on the button.
 * @property {Image} image - The scriptui object representing the image.
 * @property {StaticText} label - The label
 * @property {DuPopup} optionsPopup - A popup for .
 * @property {Group} optionsPanel - The ScriptUI Group where to add the options (child of the popup).
 * @property {DuButton~onClick} onClick - The function to execute when the button is clicked
 * @property {DuButton~onAltClick} onAltClick - The function to execute when the button is Alt + clicked
 * @property {DuButton~onCtrlClick} onCtrlClick - The function to execute when the button is Ctrl + clicked
 * @property {DuButton~onCtrlAltClick} onCtrlAltClick - The function to execute when the button is Ctrl + Alt + clicked
 * @property {DuButton~onOptions} onOptions - The function to execute when the options are called (Shift+CLick or click on the options button)
 * @category DuScriptUI
 */

/**
 * The function to execute when the button is clicked.
 * @callback DuButton~onClick
 * @memberof DuButton
 */

/**
 * The function to execute when the button is Alt + clicked.
 * @callback DuButton~onAltClick
 * @memberof DuButton
 */

/**
 * The function to execute when the button is Ctrl + clicked.
 * @callback DuButton~onCtrlClick
 * @memberof DuButton
 */

/**
 * The function to execute when the button is Ctrl + Alt + clicked.
 * @callback DuButton~onCtrlAltClick
 * @memberof DuButton
 */

/**
 * The function to execute when the options are called (Shift+CLick or click on the options button).<br/>
 * Use the <code>showUI</code> parameter to build the options when the button is clicked without showing any UI.
 * @callback DuButton~onOptions
 * @param {Boolean} showUI This is set to false when the button is clicked: use this parameter to build the options without showing any UI when needed on all button clicks.
 * @memberof DuButton
 */

/**
 * Changes the background color of the button.
 * @method
 * @memberof DuButton
 * @name setBackgroundColor
 * @param {DuColor} color - the color.
 */

/**
 * Changes the text color of the button.
 * @method
 * @memberof DuButton
 * @name setTextColor
 * @param {DuColor} color - the color.
 */

/**
 * Changes the image of the button.
 * @method
 * @memberof DuButton
 * @name setImage
 * @param {File|DuBInary} image - the image. Changing the image does not work with PNG as strings, a File must be passed.
 */

/**
 * Changes the helptip of the button.
 * @method
 * @memberof DuButton
 * @name setHelpTip
 * @param {string} helptip - the helptip.
 */

/**
 * Changes the text of the button.
 * @method
 * @memberof DuButton
 * @name setText
 * @param {string} text - the text.
 */

/**
 * Creates a button with an optionnal icon. Must have at least an icon or a text, or both.
 * @param {Panel|Window|Group}		container	- The ScriptUI Object which will contain and display the button.
 * @param {string}					[text]		- The label of the button. Default: empty string
 * @param {string|DuBInary}			[image]		- The path to the icon (or a PNG as a string representation). Default: empty string
 * @param {string}					[helpTip]		- The helptip. Default: empty string
 * @param {Boolean}					[addOptionsPanel=false]	- Adds a panel for options and a button to access it.
 * @param {Boolean}					[orientation='row']	- The orientation of the button (icon, text, options button). Default will be changed to 'column' if there's no text.
 * @param {Boolean}					[alignment='left']	- The alignment of the button content ('center', 'right' or 'left' for 'row', 'top' 'bottom', 'center' for column).
 * @param {Boolean}					[localize=true]	- Set to false if the text must not be translated.
 * @param {Boolean}					[ignoreUIMode=false]	- Will show texte even if the ui mode is set to > 1 in the script settings
 * @param {Boolean}					[optionsWithoutButton=false]	- Don't add an "ok" button to the options popup
 * @param {string}					[optionsButtonText]	- Change the displayed text of the bottom button of the options
 * @param {Boolean}					[optionsWithoutPanel=false]	- Don't create the options popup panel (use <code>DuButton.onOptions</code> to add your own callback when the options are requested)
 * @return {DuButton}			The image button created.
 */
DuScriptUI.button = function(container, text, image, helpTip, addOptionsPanel, orientation, alignment, ignoreUIMode, optionsWithoutButton, optionsButtonText, optionsWithoutPanel) {
    if (!isdef( container )) {
        throw 'DuScriptUI: DuScriptUI.button(container, text, image, helpTip, imageOver):\nMissing argument: container.';
    }

    if (jstype(container) != 'group' && jstype(container) != 'panel' && jstype(container) != 'window') {
        throw 'DuScriptUI: DuScriptUI.button(container, text, image, helpTip):\nTypeError.\n\ncontainer has to be a Group/Panel/Window.\n\ncontainer is ' + jstype(container) + ' with value ' + container.toString();
    }

    var options = {};
    text = def(text, '');

    if (jstype(text) != 'string') options = text;
    options.text = def(options.text, text);

    image = def(image, '');
    helpTip = def(helpTip, '');
    addOptionsPanel = def(addOptionsPanel, false);
    orientation = def(orientation, 'row');
    localize = def(localize, true);
    ignoreUIMode = def(ignoreUIMode, false);
    optionsWithoutButton = def(optionsWithoutButton, false);
    optionsWithoutPanel = def(optionsWithoutPanel, false);
    optionsButtonText = def(optionsButtonText, options.text);
    
    options.image = def(options.image, image);
    options.helpTip = def(options.helpTip, helpTip);
    options.options = def(options.options, addOptionsPanel);
    options.orientation = def(options.orientation, orientation);
    options.ignoreUIMode = def(options.ignoreUIMode, ignoreUIMode);
    options.optionsWithoutButton = def(options.optionsWithoutButton, optionsWithoutButton);
    options.optionsButtonText = def(options.optionsButtonText, optionsButtonText);
    options.optionsWithoutPanel = def(options.optionsWithoutPanel, optionsWithoutPanel);
    options.small = def(options.small, false);
    
    var uiMode = DuESF.scriptSettings.get("common/uiMode", 0);
    if (uiMode >= 2 && options.image != '' && !options.ignoreUIMode) options.text = '';

    if (!isdef( alignment )) {
        if (options.orientation == 'row' && options.text != '') alignment = 'left';
        else if (options.orientation == 'row') alignment = 'center';
        else alignment = 'top';
    }
    options.alignment = def(options.alignment, alignment);

    if (options.options) {
        if (options.helpTip != '') options.helpTip += '\n\n';
        options.helpTip += i18n._("[Shift]: More options...");
    }  

    if (options.image == '' && options.text == '') options.text = 'button';

    var duButton = container.add('group');
    duButton.orientation = options.orientation;
    duButton.alignment = ['fill', 'top'];
    duButton.spacing = 0;
    duButton.bgColor = DuColor.Color.TRANSPARENT;
    if (DuESF.host == DuESF.HostApplication.PHOTOSHOP) duButton.bgColor = DuColor.Color.APP_BACKGROUND_COLOR;
    duButton.textColor = DuColor.Color.APP_TEXT_COLOR;

    duButton.onClick = function() {};
    duButton.onAltClick = function() {};
    duButton.onCtrlAltClick = function() {};
    duButton.onCtrlClick = function() {};
    duButton.onShiftClick = function() {};
    duButton.onOptions = function() {};

    // Options panel
    if (options.options) {

        if (!options.optionsWithoutPanel) {
            // Create panel
            var optionsPanel = DuScriptUI.popUp(options.text == '' ? 'Options' : options.text);
            duButton.optionsPopup = optionsPanel;
            duButton.optionsPanel = DuScriptUI.group(optionsPanel.content, 'column');

            if (!options.optionsWithoutButton) {
                DuScriptUI.separator(optionsPanel.content);

                duButton.optionsButton = DuScriptUI.button( optionsPanel.content, {
                    text: options.optionsButtonText,
                    image: w12_check,
                    helpTip: options.helpTip
                });

                duButton.optionsButton.onClick = function() {
                    duButton.clicked();
                };
                duButton.optionsButton.onAltClick = function() {
                    duButton.altClicked();
                };
                duButton.optionsButton.onCtrlClick = function() {
                    duButton.ctrlClicked();
                };
                duButton.optionsButton.onCtrlAltClick = function() {
                    duButton.ctrlAltClicked();
                };
            }

            optionsPanel.tieTo(duButton, true);
        }

        // Add button
        if (options.text != '' && options.orientation != 'column') {
            var optionsButton = DuScriptUI.button(
                duButton,
                '',
                w12_options,
                options.text + '\nOptions'
            )
            optionsButton.alignment = ['left', 'center'];

            if (!options.optionsWithoutPanel) optionsPanel.tieTo(optionsButton);

            optionsButton.onClick = function () { duButton.onOptions(true); };
        }
    }

    var mainGroup = DuScriptUI.group(duButton, options.orientation);
    mainGroup.alignment = ['fill', 'fill'];
    mainGroup.margins = 0;

    //Add fillers to be able to click anywhere on the button
    if (options.text != '' && options.orientation == 'row') {
        duButton.fillerL = mainGroup.add('statictext', undefined, " ");
        duButton.fillerL.alignment = ['left', 'fill'];
        if (options.alignment == 'left' && !options.options && !options.small) duButton.fillerL.size = [18, -1];
        else if (options.alignment == 'left') duButton.fillerL.size = [2, -1];
    }

    // Add options.image
    if (options.image != '') {
        duButton.imageGroup = mainGroup.add('group');
        duButton.imageGroup.margins = 0;
        duButton.imageGroup.spacing = 0;
        duButton.imageGroup.alignment = ['fill', 'fill'];
        var subgroup = duButton.imageGroup.add('group');
        subgroup.margins = 2;
        subgroup.spacing = 0;
        subgroup.alignment = ['fill', 'fill'];
        if (options.image instanceof DuBinary) options.image = options.image.binAsString;
        duButton.image = subgroup.add('image', undefined, options.image);
        if (options.orientation == 'row') duButton.imageGroup.alignment = [options.alignment, 'center'];
        else duButton.imageGroup.alignment = ['center', options.alignment];
        // Bigger buttons on photoshop
        if (DuESF.host == DuESF.HostApplication.PHOTOSHOP) duButton.imageGroup.margins = 3;
        duButton.image.helpTip = options.helpTip;
    }

    // Another filler
    if (options.text != '' && options.image != '' && options.orientation == 'row') {
        duButton.fillerM = mainGroup.add('statictext', undefined, " ");
        duButton.fillerM.alignment = ['left', 'fill'];
        if (options.small) duButton.fillerM.size = [2, -1];
        else duButton.fillerM.size = [8, -1];
    }

    // Add text
    if (options.text != '') {
        duButton.label = mainGroup.add('statictext', undefined, options.text);
        duButton.label.helpTip = options.helpTip;
        if (options.orientation == 'row' && !options.small) duButton.label.alignment = [options.alignment, 'center'];
        else if (options.orientation == 'row') duButton.label.alignment = [options.alignment, 'bottom'];
        else duButton.label.alignment = ['center', options.alignment];
    }

    // Another filler
    if (options.text != '' && options.orientation == 'row') {
        duButton.fillerR = mainGroup.add('statictext', undefined, " ");
        duButton.fillerR.alignment = ['fill', 'fill'];
        if (options.alignment == 'right') duButton.fillerR.size = [10, -1];
    }

    //events
    duButton.clicked = function() {
        if (options.options) {
            if (!options.optionsWithoutPanel && !duButton.optionsPopup.built) {
                duButton.optionsPopup.build();
                duButton.optionsPopup.built = true;
            }
            duButton.onOptions(false);
        }
        DuDebug.safeRun(duButton.onClick);
    }

    duButton.shiftClicked = function() {
        if (options.options) {
            if (!options.optionsWithoutPanel && !duButton.optionsPopup.built) {
                duButton.optionsPopup.build();
                duButton.optionsPopup.built = true;
            }
            duButton.onOptions(true);
        }
        DuDebug.safeRun(duButton.onShiftClick);
    }

    duButton.altClicked = function() {
        if (options.options) {
            if (!options.optionsWithoutPanel && !duButton.optionsPopup.built) {
                duButton.optionsPopup.build();
                duButton.optionsPopup.built = true;
            }
            duButton.onOptions(false);
        }
        DuDebug.safeRun(duButton.onAltClick);
    }

    duButton.ctrlAltClicked = function() {
        if (options.options) {
            if (!options.optionsWithoutPanel && !duButton.optionsPopup.built) {
                duButton.optionsPopup.build();
                duButton.optionsPopup.built = true;
            }
            duButton.onOptions(false);
        }
        DuDebug.safeRun(duButton.onCtrlAltClick);
    }

    duButton.ctrlClicked = function() {
        if (options.options) {
            if (!options.optionsWithoutPanel && !duButton.optionsPopup.built) {
                duButton.optionsPopup.build();
                duButton.optionsPopup.built = true;
            }
            duButton.onOptions(false);
        }
        DuDebug.safeRun(duButton.onCtrlClick);
    }

    duButton.highlight = function(e) {
        e.stopPropagation();
        DuScriptUI.dimControls();

        if (duButton.label && !options.small) {
            DuScriptUI.setTextColor(duButton.label, DuColor.Color.APP_HIGHLIGHT_COLOR);
        }
        else {
            DuScriptUI.setBackgroundColor(mainGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker());
        }
        
        DuScriptUI.highlightedControls.push(duButton);
    }

    duButton.dim = function() {
        DuScriptUI.setBackgroundColor(mainGroup, duButton.bgColor);
        if (duButton.label) DuScriptUI.setTextColor(duButton.label, duButton.textColor);
    }

    duButton.setBackgroundColor = function(color) {
        duButton.bgColor = color;
        duButton.dim();
    }

    duButton.setTextColor = function(color) {
        duButton.textColor = color;
        duButton.dim();
    }

    duButton.setImage = function(image) {
        if (options.image == '') return;
        if (image instanceof DuBinary) image = image.toFile();
        duButton.image.image = image;
    }

    duButton.setHelpTip = function(helpTip) {
        if (options.image != '') duButton.image.helpTip = helpTip;
        if (options.text != '') duButton.label.helpTip = helpTip;
    }

    duButton.setText = function(text) {
        if (options.text != '') duButton.label.text = text;
    }

    //add events
    function clickEvent(e) {
        // On After Effects, event is triggered multiple times...
        if (e.view.parent != mainGroup && DuESF.host == DuESF.HostApplication.AFTER_EFFECTS) return;

        if (e.shiftKey) duButton.shiftClicked();
        else if (e.altKey && e.ctrlKey) duButton.ctrlAltClicked();
        else if (e.altKey) duButton.altClicked();
        else if (e.ctrlKey) duButton.ctrlClicked();
        else duButton.clicked();
    }

    mainGroup.addEventListener('click', clickEvent, true);

    mainGroup.addEventListener("mouseover", duButton.highlight);

    duButton.dim();

    return duButton;
}

/**
 * @class
 * @name DuSmallButton
 * @classdesc For use with {@link DuScriptUI}.<br />
 * An Small Button.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.smallbutton} to create a Small Button.<br />
 * The Small Button inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {string} helpLink - A URL to a help page, shown on shift click on the button.
 * @property {StaticText} label - The label
 * @property {any} value - A user value stored in the button, which is passed to the onClick method
 * @property {DuSmallButton~onClick} onClick - The function to execute when the button is clicked
 * @category DuScriptUI
 */

/**
 * The function to execute when the button is clicked.<br />
 * The implementation of this function can take one parameter which is the used defined value of the button.
 * @callback DuSmallButton~onClick
 * @memberof DuSmallButton
 * @param {any} value - The used defined value of the button
 */

/**
 * Creates a small button.
 * @param {Panel|Window|Group}			container	- The ScriptUI Object which will contain and display the button.
 * @param {string}					text		- The label of the button. Default: empty string
 * @param {string}					[helpTip]		- The helptip. Default: empty string
 * @param {any}					[value]		- A user value stored in the button, which is passed to the onClick method
 * @return {DuButton}			The image button created.
 */
DuScriptUI.smallbutton = function(container, text, helpTip, value) {
    if (!isdef( helpTip )) helpTip = '';

    var smallButton = container.add('group');
    smallButton.orientation = 'row';
    smallButton.margins = 0;
    smallButton.alignment = ['fill', 'top'];
    smallButton.alignChildren = ['fill', 'fill'];
    smallButton.spacing = 0;

    smallButton.onClick = function( v ) {};
    smallButton.onAltClick = smallButton.onClick;
    smallButton.onCtrlAltClick = smallButton.onClick;
    smallButton.onCtrlClick = smallButton.onClick;
    smallButton.label = smallButton.add('statictext', undefined, text);
    smallButton.label.helpTip = helpTip;
    smallButton.label.justify = 'center';

    smallButton.value = value;
    smallButton.helpLink = '';

    //events
    smallButton.clicked = function() {
        smallButton.onClick( smallButton.value );
    }

    smallButton.shiftClicked = function() {
        if (duButton.helpLink != '') {
            DuSystem.openURL(smallButton.helpLink);
        }
    }

    smallButton.altClicked = function() {
        smallButton.onAltClick( smallButton.value );
    }

    smallButton.ctrlAltClicked = function() {
        smallButton.onCtrlAltClick( smallButton.value );
    }

    smallButton.ctrlClicked = function() {
        smallButton.onCtrlClick( smallButton.value );
    }

    smallButton.highlight = function(e) {
        e.stopPropagation();
        DuScriptUI.dimControls();

        if (smallButton.label) DuScriptUI.setTextColor(smallButton.label, DuColor.Color.VERY_DARK_GREY);
        DuScriptUI.setBackgroundColor(smallButton, DuColor.Color.LIGHT_GREY);

        DuScriptUI.highlightedControls.push(smallButton);
    }

    smallButton.dim = function() {
        if (smallButton.label) DuScriptUI.setTextColor(smallButton.label, DuColor.Color.APP_TEXT_COLOR);
        DuScriptUI.setBackgroundColor(smallButton, DuColor.Color.TRANSPARENT);
    }

    //add events
    smallButton.addEventListener('click', function(e) {
        if (e.shiftKey) smallButton.shiftClicked();
        else if (e.altKey && e.ctrlKey) smallButton.ctrlAltClicked();
        else if (e.altKey) smallButton.altClicked();
        else if (e.ctrlKey) smallButton.ctrlClicked();
        else smallButton.clicked();
    }, true);


    smallButton.addEventListener("mouseover", smallButton.highlight);
    if (smallButton.label) smallButton.label.addEventListener("mouseover", smallButton.highlight);

    return smallButton;
}// ==================== |------| ====================
// ==================== | help | ====================
// ==================== |------| ====================

/**
 * Creates a button displaying the version of the script and redirecting to the about url.
 * @param {Panel|Window|Group} container - The ScriptUI Object which will contain and display the button.
 * @param {string|DuBinary} [image=DuESF.scriptIcon] - The path to the icon or a png binstring. Default: empty string
 * @return {DuButton} The version button created.
 */
DuScriptUI.versionButton = function( container, image )
{
    image = def(image, DuESF.scriptIcon);

    var button = DuScriptUI.button(
        container,
        'v' + DuESF.scriptVersion.fullVersion,
        image,
        DuESF.companyName + '\n' + DuESF.scriptName + ' v' + DuESF.scriptVersion.fullVersion,
        false,
        undefined,
        undefined,
        false
    )

    //button.bgColor = DuColor.Color.TRANSPARENT;
    button.dim();

    if (DuESF.aboutURL != '') button.onClick = function() {
        DuSystem.openURL(DuESF.aboutURL);
    };

    return button;
}

/**
 * Creates a button opening the bug report url.
 * @param {Panel|Window|Group} container - The ScriptUI Object which will contain and display the button.
 * @param {bool} [showLabel=false] - When true, the button includes a "Bug Report" label.
 * @return {DuButton} The bug button created.
 */
DuScriptUI.addBugButton = function( container, showLabel )
{
    showLabel = def(showLabel, false);

    var label = i18n._("Bug Report or Feature Request");
    var helpTip = i18n._("Bug report\nFeature request\n\nTell us what's wrong or request a new feature.");

    var button;

    if (container.addButton) 
    {
        button = container.addButton(
            label,
            w12_bugreport,
            helpTip
        );
    }
    else 
    {
        button = DuScriptUI.button(
            container,
            showLabel ? label : "",
            w12_bugreport,
            helpTip
            );
    }

    button.onClick = function ()
    {
        DuSystem.openURL( DuESF.bugReportURL );
    }
    button.onAltClick = function ()
    {
        DuSystem.openURL( 'http://bat-ultra-bunny.tumblr.com' );
    }
    return button;
}

/**
 * Creates a button opening the help panel.
 * @param {Panel|Window|Group} container - The ScriptUI Object which will contain and display the button.
 * @param {bool} [showLabel=false] - When true, the button includes a "Help" label.
 * @return {DuButton} The help button created.
 */
DuScriptUI.addHelpButton = function( container, showLabel )
{
    showLabel = def(showLabel, false);

    var label = i18n._("Help");
    var helpTip = i18n._("Get help.");

    var button;

    if (container.addButton) 
    {
        button = container.addButton(
            label,
            w12_help,
            helpTip
        );
    }
    else 
    {
        button = DuScriptUI.button(
            container,
            showLabel ? label : "",
            w12_help,
            helpTip
            );
    }

    button.onClick = function ()
    {
        DuSystem.openURL( DuESF.docURL );
    }
    button.onAltClick = function ()
    {
        DuSystem.openURL( 'http://bat-ultra-bunny.tumblr.com' );
    }

    return button;
}

/**
 * Creates a button opening the help panel.
 * @param {Panel|Window|Group} container - The ScriptUI Object which will contain and display the button.
 * @param {bool} [showLabel=false] - When true, the button includes a "Help" label.
 * @return {DuButton} The help button created.
 */
DuScriptUI.addTranslateButton = function( container, showLabel )
{
    showLabel = def(showLabel, false);

    var label = i18n._("Translate %1", DuESF.scriptName);
    var helpTip = i18n._("Help translating %1 to make it available to more people.", DuESF.scriptName);

    var button;

    if (container.addButton) 
    {
        button = container.addButton(
            label,
            w12_language,
            helpTip
        );
    }
    else 
    {
        button = DuScriptUI.button(
            container,
            showLabel ? label : "",
            w12_language,
            helpTip
            );
    }

    button.onClick = function ()
    {
        DuSystem.openURL( DuESF.translateURL );
    }
    button.onAltClick = function ()
    {
        DuSystem.openURL( 'http://bat-ultra-bunny.tumblr.com' );
    }

    return button;
}

/**
 * Creates a button opening the link for like/follow/donation
 * @param {Panel|Window|Group} container - The ScriptUI Object which will contain and display the button.
 * @param {bool} [showLabel=false] - When true, the button includes a <code>"I ♥ " + DuESF.scriptName</code> label.
 * @return {DuButton} The like button created.
 */
DuScriptUI.addDonateButton = function ( container, showLabel )
{
    showLabel = def(showLabel, false);

    var defaultLabel = "I \u2665 " + DuESF.scriptName;

    var button = DuScriptUI.button(
        container,
        {
            text: showLabel ? defaultLabel : "",
            image: w12_heart,
            helpTip: defaultLabel,
            small: true
        }
    );

    DuScriptUI.fundingLabel = button.label;

    button.onClick = function ()
    {
        DuSystem.openURL( DuESF.donateURL );
    }
    button.onAltClick = function ()
    {
        DuSystem.openURL( 'http://patreon.com/duduf' );
    }

    DuScriptUI.updateFundingInfo();

    return button;
}

/**
 * Displays a prompt to select the language of the script.<br />
 * Won't do nothing if the script already has a language set in the settings.<br />
 * Use this method before launching the script. 
 * @param {function} callback - The function to execute when the user has chosen the language.<br />
 * This function should be the one which loads the script.
 * @param {Panel|Window} [ui]	- A container to display the UI. A modal Dialog is created if omitted
 */
DuScriptUI.askLanguage = function ( callback, ui )
{
    if (! (ui instanceof Panel || ui instanceof Window) )
    {
        ui = new Window('dialog', "Language")
    }

    var currentLanguageName = DuESF.scriptSettings.get("common/currentLanguageName", '');
    var currentLocale = DuESF.scriptSettings.get("common/language", '');

    var languages = i18n.getAvailableLanguages();

    if (currentLanguageName != '' || languages.count == 0)
    {
        callback()
        return;
    }

    var ui_languageGroup = DuScriptUI.group( ui, 'column' );

    ui_languageGroup.add('statictext', undefined, "EO: Selektu la lingvo kiu vi volas uzi per tio softvaro.", { multiline: true } );
    ui_languageGroup.add('statictext', undefined, "EN: Select the language you wish to use with this software.", { multiline: true } );

    var currentIndex = 0;
    var languageSelector = DuScriptUI.selector( ui_languageGroup, "Set the language of the interface." );
    var i = 0;
    for (var locale in languages)
    {
        if (locale == "count") continue;
        languageSelector.addButton( {
            text: languages[locale],
            image: w16_language,
            data: locale
        });
        if (locale == 'eo') currentIndex = i;
        i++;
    }

    languageSelector.setCurrentIndex( currentIndex );

    var okButton = DuScriptUI.button( ui_languageGroup, {
        text: "Apply",
        image: w12_check,
        helpTip: "Apply changes to the settings.",
        alignment: 'center'
    });

    okButton.onClick = function()
    {
        // Save settings
        DuESF.scriptSettings.set("common/language", languageSelector.currentData );
        DuESF.scriptSettings.set("common/currentLanguageName", languageSelector.text);
        i18n.setLocale( languageSelector.currentData );

        DuESF.scriptSettings.save();

        // Delete
        ui.remove( ui_languageGroup );

        callback();
    }

    DuScriptUI.showUI( ui, false );
    DuScriptUI.uiShown = false;
}

/**
 * Checks if the script can be updated
 * @param {function} [callback] - The function to execute when the user has clicked on the "dismiss" button.
 * @param {Panel|Window} [ui]	- A container to display the UI. A modal Dialog is created if omitted
 * @param {Boolean} [showAlert] Whether to show an alert if the check failed or if the version is up-to-date.
 */
DuScriptUI.checkUpdate = function ( callback, ui, showAlert )
{
    var noCallback = false;
    if (!isdef( callback ))
    {
        callback = function(){};
        var noCallback = true;
    }
    showAlert = def(showAlert, false);
    //if (DuESF.debug) showAlert = true;

    //Just once a day
    var latestUpdateCheck = DuESF.scriptSettings.get("common/latestUpdateCheck", 0);
    var now = Date.now();
    // 24h
    if (now - latestUpdateCheck < 86400000 && !showAlert && !DuESF.debug) {
        // Just update funding info
        DuScriptUI.updateFundingInfo();
        return callback();
    }
    DuESF.scriptSettings.set("common/latestUpdateCheck", now);
    DuESF.scriptSettings.save();

    if ( DuESF.rxVersionURL == '' ) return callback();

    // Get the version & info from the server
    var update = DuVersion.checkUpdate();

    // Compare
    if (!update)
    {
        if (noCallback) return;
        return callback();
    }
    if (!update.success)
    {
        if (showAlert) alert("Sorry, something failed...\n\n" + update.message);
        if (noCallback) return;
        return callback();
    }

    // Update funding info
    if (update.monthlyFund && update.fundingGoal) {
        DuScriptUI.updateFundingInfo(update.monthlyFund, update.fundingGoal);
    }

    if (!update.update)
    {
        if (showAlert) alert("This version is up-to-date, congratulations!");
        if (noCallback) return;
        return callback();
    }

    // Show window if update
    var reuseUI = true;
    if (! (ui instanceof Panel || ui instanceof Window) )
    {
        reuseUI = false;
        ui = new Window('dialog',i18n._("New version"),undefined,
        {
            resizeable: true
        } );
    }

    var ui_updateGroup = DuScriptUI.group( ui, 'column' );
    ui_updateGroup.alignment = ['fill', 'fill'];

    var titleGroup = DuScriptUI.group( ui_updateGroup );
    titleGroup.alignment = ['fill', 'top'];
    DuScriptUI.setBackgroundColor( titleGroup, DuColor.Color.DARK_GREY );

    DuScriptUI.staticText(
        titleGroup,
        DuString.args( "New {#}!", [ DuESF.scriptName ] ),
        DuColor.Color.APP_TEXT_COLOR.lighter(130)
        ).alignment = ['center','top'];

    var newVersionGroup = DuScriptUI.group( ui_updateGroup );
    newVersionGroup.alignment = ['fill', 'top'];
    newVersionGroup.margins = 2;

    DuScriptUI.staticText(
        newVersionGroup,
        "New version" + ": "
        );

    DuScriptUI.staticText(
        newVersionGroup,
        update.version,
        DuColor.Color.APP_TEXT_COLOR.lighter(115)
        );

    var descriptionGroup = DuScriptUI.group( ui_updateGroup );
    descriptionGroup.margins = 2;
    descriptionGroup.alignment = ['fill','fill'];
    DuScriptUI.setBackgroundColor( descriptionGroup, DuColor.Color.OBSIDIAN );

    var descriptionText = descriptionGroup.add("edittext", undefined, update.description, {multiline:true});
    descriptionText.alignment = ['fill','fill'];
    descriptionText.minimumSize = [-1,100];
    DuScriptUI.setBackgroundColor( descriptionText, DuColor.Color.OBSIDIAN );

    DuScriptUI.staticText(
        ui_updateGroup,
        "Current version: " + DuESF.scriptVersion.fullVersion,
        DuColor.Color.APP_TEXT_COLOR.darker(160)
        ).alignment = ['fill', 'bottom'];

    if (update.downloadURL)
    {
        var downloadButton = DuScriptUI.button(
            ui_updateGroup,
            "Download",
            DuScriptUI.Icon.DOWNLOAD,
            "Download the new version now."
        );
        downloadButton.alignment = ['fill','bottom'];

        downloadButton.onClick = function() {
            DuSystem.openURL( update.downloadURL );
        };
    }
    
    if (update.changelogURL)
    {
        var changelogButton = DuScriptUI.button(
            ui_updateGroup,
            "Changelog",
            DuScriptUI.Icon.LIST,
            "Read all the details about what's changed."
        );
        changelogButton.alignment = ['fill','bottom'];

        changelogButton.onClick = function() {
            DuSystem.openURL( update.changelogURL );
        };
    }

    if (update.donateURL)
    {
        var donateButton = DuScriptUI.button(
            ui_updateGroup,
            "I \u2665 " + DuESF.scriptName,
            DuScriptUI.Icon.HEART,
            "We need you!\nMake a donation to help us continue to release and update free software."
        );
        donateButton.alignment = ['fill','bottom'];

        donateButton.onClick = function()
        {
            DuSystem.openURL( update.donateURL );
        };
        donateButton.onAltClick = function ()
        {
            DuSystem.openURL( 'http://patreon.com/duduf' );
        }
    }

    var validButton = DuScriptUI.button(
        ui_updateGroup,
        noCallback ? "Close" : DuString.args("Update later"),
        noCallback ? DuScriptUI.Icon.CLOSE : DuScriptUI.Icon.NEXT,
        noCallback ? "Close" : DuString.args("Continue to {#}", [DuESF.scriptName])
    );
    validButton.onClick = function()
    {
        // Delete or hide ui
        if (reuseUI) ui.remove( ui_updateGroup );
        else ui.close();
        
        if (!noCallback) callback();

    }
    
    DuScriptUI.showUI( ui, false );
    DuScriptUI.uiShown = false;
}

DuScriptUI.updateFundingInfo = function (fund, goal) {
   
    fund = def(fund, DuESF.scriptSettings.get("funding/monthlyFund", 0));
    goal = def(goal, DuESF.scriptSettings.get("funding/fundingGoal", 4000));

    if (fund) {
        fund = parseFloat(fund);
        DuESF.scriptSettings.set("funding/monthlyFund", fund);
    }
    if (goal) {
        goal = parseFloat(goal);
        DuESF.scriptSettings.set("funding/fundingGoal", goal);
    }

    DuESF.scriptSettings.save();

    if (!fund || !goal && DuScriptUI.fundingBar) {
        DuScriptUI.fundingBar.value = 0;
        DuScriptUI.fundingBar.helpTip = i18n._("Thank you for your donations!");
        return;
    }

    var ratio = Math.round( fund / goal * 100);
    var helpTip = i18n._("Thank you for your donations!") + '\n\n' + 
            i18n._( "This month, the %1 fund is $%2.\nThat's %3% of our monthly goal ( $%4 )\n\nClick on this button to join the development fund!", DuESF.companyName, fund, ratio, goal );

    if (DuScriptUI.fundingBar) {
        DuScriptUI.fundingBar.value = ratio;
        fund = Math.round( fund );
        DuScriptUI.fundingBar.helpTip = helpTip
    }
    if (DuScriptUI.fundingLabel) {
        DuScriptUI.fundingLabel.helpTip = helpTip;
        DuScriptUI.fundingLabel.text = ratio + '%';
    }
    
}// ==================== |-------| ====================
// ==================== | style | ====================
// ==================== |-------| ====================

/**
 * Resets the layout of the whole ui containing a scriptUI item.
 * @param {ScriptUI} item - The ScriptUI element which needs to be resized
 * @param {Boolean} [force] - Needs to be true if you need to layout before <code>DuESF.state</code> is <code>DuESF.State.RUNTIME</code>.<br />
 * That should be the case only for the main UI; Note that you should not need it anyway, {@link DuScriptUI.showUI} does that for you.
 */
DuScriptUI.layout = function( item, force )
{
    force = def( force, false );
    if (DuESF.state != DuESF.State.RUNTIME && !force) return;
    item.layout.layout( true );
    item.layout.resize();
}

/**
 * Changes the color of the text of a ScriptUI Object
 * @param {ScriptUI} text - The ScriptUI Object
 * @param {DuColor} color - The new color
 * @param {Boolean} [adjusted=false] - lightens the color if the brightness setting of Ae is not set on the darkest one
 */
DuScriptUI.setTextColor = function( text, color, adjusted )
{
    if ( !isdef( text ) ) throw "You must provide a ScriptUI Control to change the color of the text";
    adjusted = def( adjusted, false );

    var g = text.graphics;
    var c;
    if (adjusted) c = color.adjusted();
    else c = color;
    var textPen = g.newPen( g.PenType.SOLID_COLOR, c.floatRGBA(true), 1 );
    g.foregroundColor = textPen;
}

/**
 * Changes the color of the background of a ScriptUI Object
 * @param {ScriptUI} uiItem - The ScriptUI Object
 * @param {Array} color - The new color [R,V,B,A] Array
 * @param {Boolean} [adjusted=true] - lightens the color if the brightness setting of Ae is not set on the darkest one
 */
DuScriptUI.setBackgroundColor = function( uiItem, color, adjusted )
{
    if ( !isdef( uiItem ) ) throw "You must provide a ScriptUI Control to change the color of the background";
    adjusted = def( adjusted, true );
   
    var g = uiItem.graphics;
    var c;
    if (adjusted) c = color.adjusted();
    else c = color;
    var brush = g.newBrush( g.BrushType.SOLID_COLOR, c.floatRGBA(true) );
    g.backgroundColor = brush;
}// ==================== |--------| ====================
// ==================== | layout | ====================
// ==================== |--------| ====================


/**
 * Adds a group in a container, using  DuScriptUI default alignments, and DuScriptUI.defaultSpacing. Margins are set to 0.
 * @param {Panel|Window|Group} container Where to create the group
 * @param {string} [orientation] The orientation to use. One of "column", "row" or "stack". By default, "column" if added in a row, "row" if added in a column.
 * @return {Group}	The group created
 */
DuScriptUI.group = function( container, orientation )
{
    var group = container.add( "group" );
    group.spacing = 0;
    group.margins = 0;
    if ( orientation !== undefined ) group.orientation = orientation;
    if ( group.orientation === "row" )
    {
        group.alignChildren = DuScriptUI.defaultRowAlignment;
    }
    else if ( group.orientation === "column" )
    {
        group.alignChildren = DuScriptUI.defaultColumnAlignment;
    }
    else
    {
        group.alignChildren = DuScriptUI.defaultStackAlignment;
    }

    group.addEventListener( "mouseover", DuScriptUI.dimControls );

    return group;
}

/**
 * @class
 * @name DuSeparator
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A DuSeparator.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.separator} to create a DuSeparator.<br />
 * The DuSeparator inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {StaticText|CheckBox} label - the label
 * @property {boolean} checkable - Read-Only | true if a checkbox is displayed
 * @category DuScriptUI
 */

/**
 * Adds separator with an optionnal name in the group
 * @memberof DuScriptUI
 * @param {Panel|Window|Group} container - Where to create the separator
 * @param {string} [name] - The name displayed
 * @param {boolean} [checkable=false] - When true, adds a checkbox to the separator
 * @param {boolean} [drawLine=true] - Draws a line when there is no name. When false, the separator is an empty space
 * @param {boolean} [translatable=true] - If false, the name won't be translated
 * @return {DuSeparator} The separator
 */
DuScriptUI.separator = function( container, name, checkable, drawLine )
{
    name = def(name, '' );
    checkable = def( checkable, false );
    drawLine = def( drawLine, true );

    var separator = DuScriptUI.group( container );
    separator.margins = 0;
    separator.checkable = checkable;

    separator.label = null;
    if ( name != '' || checkable )
    {
        if ( checkable ) separator.label = separator.add( 'checkbox', undefined, name );
        else separator.label = separator.add( 'statictext', undefined, name );
        separator.label.alignment = [ 'center', 'bottom' ];
        if ( drawLine ) DuScriptUI.setBackgroundColor( separator, DuColor.Color.DARK_GREY );
    }
    else if ( drawLine )
    {
        DuScriptUI.setBackgroundColor( separator, DuColor.Color.ABYSS_GREY );
        var size = 1;
        if (DuESF.host == DuESF.HostApplication.AFTER_EFFECTS) size = 2;
        if (container.orientation == 'row')
        {
            separator.alignment = ['left', 'fill'];
            separator.minimumSize = [size,-1];
        }
        else
        {
            separator.alignment = [ 'fill', 'top' ];
            separator.minimumSize = [-1,size];
        }
    }

    separator.addEventListener( "mouseover", DuScriptUI.dimControls );

    return separator;
}
// ==================== |----------| ====================
// ==================== | checkbox | ====================
// ==================== |----------| ====================


/**
 * @class
 * @name DuCheckBox
 * @classdesc For use with {@link DuScriptUI}.<br />
 * An Image Checkbox.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.checkBox} to create an Image Checkbox.<br />
 * The Image Checkbox inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {Image} image The scriptui object representing the image, if any
 * @property {StaticText} label The label, if any
 * @property {string} text The current text
 * @property {string} textChecked The text to display when the checkbox is checked
 * @property {string} defaultText The text to display when the checkbox is unchecked
 * @property {boolean} checked The checked state of the button
 * @property {DuColor} textColor The color of the text of the label
 * @property {DuCheckBox~onClick} onClick The function to execute when the button is clicked
 * @category DuScriptUI
 */

/**
 * The function to execute when the button is clicked.
 * @callback DuCheckBox~onClick
 * @memberof DuCheckBox
 */

/**
 * Sets the checked state of the button
 * @method
 * @memberof DuCheckBox
 * @name setChecked
 * @param {boolean} [checked=true] - The state
 */

/**
 * Creates a checkbox with an optionnal icon. Must have at least an icon or a text, or both.
 * @param {Panel|Window|Group} container The ScriptUI Object which will contain and display the button.
 * @param {string} text The label of the button. Default: empty string
 * @param {string|DuBinary} [image] The path to the icon. Default: empty string
 * @param {string} [helpTip=''] The helptip. Default: empty string
 * @param {string} [textChecked=text] The label of the button displayed when it is checked.
 * @param {string|DuBinary} [imageChecked] The image to show when it is checked
 * @param {string} [orientation='row'] The orientation
 * @return {DuCheckBox} The checkbox created.
 */
DuScriptUI.checkBox = function( container, text, image, helpTip, textChecked, imageChecked, orientation, isTab )
{
    var options = {};
    text = def(text, '');
    if (jstype(text) != 'string') options = text;
    options.text = def(options.text, text);
    
    isTab = def(isTab, false);
    options.isTab = def(options.isTab, isTab);

    orientation = def(orientation, 'row');
    options.orientation = def(options.orientation, orientation);
    
    textChecked = def(textChecked, options.text);
    options.textChecked = def(options.textChecked, textChecked);

    image = def(image, '');
    options.image = def(options.image, image);

    imageChecked = def(imageChecked, options.image);
    options.imageChecked = def(options.imageChecked, imageChecked);

    helpTip = def(helpTip, '');
    options.helpTip = def(options.helpTip, helpTip);

    if ( !container ) return null;

    if (options.isTab || options.orientation == 'column')
    {
        var commonSettings = def( DuESF.scriptSettings.data.common, {} );
        var uiMode = def(commonSettings.uiMode, 0);
        if (uiMode >= 2 && options.image != '') text = '';
    }

    //create ui
    var duCheckBox = container.add( 'group' );
    duCheckBox.orientation = 'column';

    if (options.isTab || options.orientation == 'column') duCheckBox.alignment = ['left', 'fill'];
    else duCheckBox.alignment = [ 'fill', 'top' ];

    if (options.isTab || options.text == '' || options.orientation == 'column' ) duCheckBox.hasIcon = false;
    else duCheckBox.hasIcon = true;

    duCheckBox.hasImage = options.image != '';
    duCheckBox.hasLabel = options.text != '';
    duCheckBox.imageOnly = options.image == options.imageChecked && options.text == '';

    //if no options.image and no options.text, at least the icon
    if ( options.image == '' && options.text == '' ) duCheckBox.hasIcon = true;

    // attributes
    duCheckBox.onClick = function() {};
    duCheckBox.onAltClick = function() {};
    duCheckBox.checked = duCheckBox.value = false;
    duCheckBox.textChecked = options.textChecked;
    duCheckBox.defaultText = options.text;
    duCheckBox.text = options.text;
    duCheckBox.helpLink = '';
    duCheckBox.textColor = DuColor.Color.APP_TEXT_COLOR;

    duCheckBox.mainGroup = DuScriptUI.group(duCheckBox, 'column');
    duCheckBox.mainGroup.margins = 1;
    duCheckBox.mainGroup.alignment = ['fill', 'fill'];
    
    duCheckBox.buttonGroup = DuScriptUI.group(duCheckBox.mainGroup, options.orientation);
    duCheckBox.buttonGroup.alignment = ['fill', 'top'];

    //the checkbox icon & options.image group
    duCheckBox.imageGroup = duCheckBox.buttonGroup.add('group');
    duCheckBox.imageGroup.margins = 0;
    duCheckBox.imageGroup.spacing = 4;
    if ( options.text == '' || options.orientation == 'column' ) duCheckBox.imageGroup.alignment = [ 'center', 'center' ];
    else duCheckBox.imageGroup.alignment = [ 'left', 'center' ];

    if (duCheckBox.hasIcon)
    {
        var subgroup = duCheckBox.imageGroup.add('group');
        subgroup.orientation = 'stacked';
        subgroup.margins = 0;
        subgroup.spacing = 0;
        subgroup.size = [12,12];

        duCheckBox.checkboxIcon = subgroup.add( 'image', undefined, DuScriptUI.Icon.BOX_UNCHECKED.binAsString );
        duCheckBox.checkboxIcon.helpTip = options.helpTip;

        duCheckBox.checkboxIconChecked = subgroup.add( 'image', undefined, DuScriptUI.Icon.BOX_CHECKED.binAsString );
        duCheckBox.checkboxIconChecked.helpTip = options.helpTip;
        duCheckBox.checkboxIconChecked.visible = false;
    }

    //checkbox icon
    if ( duCheckBox.hasImage )
    {
        var subgroup = duCheckBox.imageGroup.add('group');
        subgroup.orientation = 'stacked';
        subgroup.margins = 2;
        subgroup.spacing = 0;
        subgroup.minimumSize = [20,20];

        if ( options.image instanceof DuBinary ) options.image = options.image.binAsString;
        if ( options.imageChecked instanceof DuBinary ) options.imageChecked = options.imageChecked.binAsString;

        if (options.imageChecked != '')
        {
            duCheckBox.imageChecked = subgroup.add( 'image', undefined, options.imageChecked );
            duCheckBox.imageChecked.helpTip = options.helpTip;
            duCheckBox.imageChecked.visible = false;
            duCheckBox.imageChecked.alignment = ['center', 'center'];
        }
        
        duCheckBox.image = subgroup.add( 'image', undefined, options.image);
        duCheckBox.image.helpTip = options.helpTip;
        duCheckBox.image.alignment = ['center', 'center'];
    }
 
    //Add fillers to be able to click anywhere on the button
    if ( duCheckBox.hasLabel && !options.isTab && options.orientation != 'column')
    {
        duCheckBox.fillerM = duCheckBox.buttonGroup.add( 'statictext', undefined, " " );
        duCheckBox.fillerM.alignment = [ 'left', 'fill' ];
        duCheckBox.fillerM.size = [ 7, -1];
    }

    // label
    if ( duCheckBox.hasLabel )
    {
        var labelGroup = duCheckBox.buttonGroup.add('group');
        labelGroup.orientation = 'stacked';
        labelGroup.margins = 0;
        if (options.orientation == 'column') labelGroup.alignment = [ 'center', 'top' ];
        else labelGroup.alignment = [ 'left', 'fill' ];

        duCheckBox.label = labelGroup.add( 'statictext', undefined, options.text );
        duCheckBox.label.helpTip = options.helpTip;
        if (options.orientation == 'column') duCheckBox.label.alignment = [ 'center', 'top' ];
        else duCheckBox.label.alignment = [ 'left', 'fill' ];
        DuScriptUI.setTextColor( duCheckBox.label, DuColor.Color.APP_TEXT_COLOR );
        
        duCheckBox.labelChecked = labelGroup.add( 'statictext', undefined, options.textChecked );
        duCheckBox.labelChecked.helpTip = options.helpTip;
        duCheckBox.labelChecked.visible = false;
        if (options.orientation == 'column') duCheckBox.labelChecked.alignment = [ 'center', 'top' ];
        else duCheckBox.labelChecked.alignment = [ 'left', 'fill' ];
        DuScriptUI.setTextColor( duCheckBox.labelChecked, DuColor.Color.APP_HIGHLIGHT_COLOR );
    }

    //Add fillers to be able to click anywhere on the button
    if ( duCheckBox.hasLabel && !options.isTab && options.orientation != 'column' )
    {
        duCheckBox.fillerR = duCheckBox.buttonGroup.add( 'statictext', undefined, "" );
        duCheckBox.fillerR.alignment = [ 'fill', 'fill' ];
    }

    // outline
    if ( options.isTab )
    {
        duCheckBox.outLine = duCheckBox.mainGroup.add( 'group' );
        duCheckBox.outLine.margins = 0;
        duCheckBox.outLine.minimumSize.height = 2;
        duCheckBox.outLine.alignment = [ 'fill', 'bottom' ];
    }

    duCheckBox.setChecked = function( c )
    {
        c = def(c, true);
        duCheckBox.checked = duCheckBox.value = c;

        if ( c )
        {
            if (options.isTab) DuScriptUI.setBackgroundColor( duCheckBox.outLine, DuColor.Color.APP_TEXT_COLOR );
            else 
            {
                //icon
                if ( duCheckBox.hasIcon )
                {
                    duCheckBox.checkboxIconChecked.visible = true;
                    duCheckBox.checkboxIcon.visible = false;
                }

                //image
                if ( duCheckBox.hasImage ) 
                {
                    duCheckBox.imageChecked.visible = true;
                    duCheckBox.image.visible = false;
                    if (duCheckBox.imageOnly)
                    {
                        DuScriptUI.setBackgroundColor( duCheckBox.imageGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
                    }
                }

                //label
                if ( duCheckBox.hasLabel )
                {
                    duCheckBox.label.visible = false;
                    duCheckBox.labelChecked.visible = true;
                    duCheckBox.text = duCheckBox.textChecked;
                    DuScriptUI.setTextColor( duCheckBox.label, DuColor.Color.APP_HIGHLIGHT_COLOR );
                }
            }
        }
        else 
        {
            if (options.isTab) DuScriptUI.setBackgroundColor( duCheckBox.outLine, DuColor.Color.TRANSPARENT );
            else 
            {
                //icon
                if ( duCheckBox.hasIcon )
                {
                    duCheckBox.checkboxIconChecked.visible = false;
                    duCheckBox.checkboxIcon.visible = true;
                }

                //image
                if ( duCheckBox.hasImage ) 
                {
                    duCheckBox.imageChecked.visible = false;
                    duCheckBox.image.visible = true;
                    if (!duCheckBox.hasIcon && !duCheckBox.hasLabel) DuScriptUI.setBackgroundColor( duCheckBox.imageGroup, DuColor.Color.TRANSPARENT );
                }

                //label
                if ( duCheckBox.hasLabel )
                {
                    duCheckBox.label.visible = true;
                    duCheckBox.labelChecked.visible = false;
                    duCheckBox.text = duCheckBox.defaultText;
                    DuScriptUI.setTextColor( duCheckBox.label, DuColor.Color.APP_TEXT_COLOR );
                }
            }
        }
    }

    duCheckBox.clicked = function( e )
    {
        e.stopPropagation();

        if ( e.shiftKey )
        {
            if ( duCheckBox.helpLink != '' )
            {
                DuSystem.openURL( duCheckBox.helpLink );
            }
            return;
        }

        duCheckBox.setChecked( !duCheckBox.checked );
        DuDebug.safeRun(duCheckBox.onClick);
    }

    duCheckBox.altClicked = function()
    {
        DuDebug.safeRun(duCheckBox.onAltClick);
    }

    duCheckBox.highlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();

        if ( duCheckBox.checked )
        {
            if (duCheckBox.hasLabel && !options.isTab) DuScriptUI.setTextColor( duCheckBox.labelChecked, DuColor.Color.APP_HIGHLIGHT_COLOR.lighter(150) );
            
            if (!duCheckBox.hasLabel && !options.isTab) DuScriptUI.setBackgroundColor( duCheckBox.buttonGroup, DuColor.Color.APP_HIGHLIGHT_COLOR );
            // image
            if (!options.isTab && duCheckBox.imageOnly) DuScriptUI.setBackgroundColor( duCheckBox.imageGroup, DuColor.Color.APP_HIGHLIGHT_COLOR );

            //outline
            if ( options.isTab )
            {
                DuScriptUI.setBackgroundColor( duCheckBox.outLine, DuColor.Color.APP_HIGHLIGHT_COLOR );
                if (duCheckBox.hasLabel) DuScriptUI.setTextColor( duCheckBox.label, DuColor.Color.APP_HIGHLIGHT_COLOR );
            }
        }
        else 
        {
            if (duCheckBox.hasLabel && !options.isTab) DuScriptUI.setTextColor( duCheckBox.label, DuColor.Color.APP_HIGHLIGHT_COLOR );

            if (!duCheckBox.hasLabel && !options.isTab) DuScriptUI.setBackgroundColor( duCheckBox.buttonGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
            // image
            if (!options.isTab && duCheckBox.imageOnly) DuScriptUI.setBackgroundColor( duCheckBox.imageGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );

            //outline
            if (options.isTab )
            {
                DuScriptUI.setBackgroundColor( duCheckBox.outLine, DuColor.Color.APP_HIGHLIGHT_COLOR );
                if (duCheckBox.hasLabel) DuScriptUI.setTextColor( duCheckBox.label, DuColor.Color.APP_HIGHLIGHT_COLOR );
            }
        }
        

        DuScriptUI.highlightedControls.push( duCheckBox );
    }

    duCheckBox.dim = function( e )
    {
        if ( duCheckBox.checked )
        {
            if (duCheckBox.hasLabel && !options.isTab) DuScriptUI.setTextColor( duCheckBox.labelChecked, DuColor.Color.APP_HIGHLIGHT_COLOR );
            
            if (!duCheckBox.hasLabel && !options.isTab) DuScriptUI.setBackgroundColor( duCheckBox.buttonGroup, DuColor.Color.TRANSPARENT );
            // image
            if (!options.isTab && duCheckBox.imageOnly) DuScriptUI.setBackgroundColor( duCheckBox.imageGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );

            //outline
            if ( options.isTab )
            {
                DuScriptUI.setBackgroundColor( duCheckBox.outLine, DuColor.Color.APP_TEXT_COLOR );
                if (duCheckBox.hasLabel) DuScriptUI.setTextColor( duCheckBox.label, duCheckBox.textColor );
            }
        }
        else 
        {
            if (duCheckBox.hasLabel && !options.isTab) DuScriptUI.setTextColor( duCheckBox.label, duCheckBox.textColor );
            
            if (!duCheckBox.hasLabel && !options.isTab) DuScriptUI.setBackgroundColor( duCheckBox.buttonGroup, DuColor.Color.TRANSPARENT );
            // image
            if (!options.isTab && duCheckBox.imageOnly) DuScriptUI.setBackgroundColor( duCheckBox.imageGroup, DuColor.Color.TRANSPARENT );

            //outline
            if ( options.isTab )
            {
                DuScriptUI.setBackgroundColor( duCheckBox.outLine, DuColor.Color.TRANSPARENT );
                if (duCheckBox.hasLabel) DuScriptUI.setTextColor( duCheckBox.label, duCheckBox.textColor );
            }
        }
    }

    duCheckBox.addEventListener( "click", function(e)
    {
        if(e.view.parent != duCheckBox) return;
        
        if ( e.altKey ) duCheckBox.altClicked();
        else duCheckBox.clicked(e);
    }, true );


    duCheckBox.addEventListener( "mouseover", duCheckBox.highlight );
    duCheckBox.buttonGroup.addEventListener( "mouseover", duCheckBox.highlight );
    duCheckBox.mainGroup.addEventListener( "mouseover", duCheckBox.highlight );
    if(duCheckBox.label) duCheckBox.label.addEventListener( "mouseover", duCheckBox.highlight );
    if(duCheckBox.checkBox) duCheckBox.imageChecked.addEventListener( "mouseover", duCheckBox.highlight );
    if(duCheckBox.image) duCheckBox.image.addEventListener( "mouseover", duCheckBox.highlight );

    duCheckBox.dim();

    return duCheckBox;
}
// ==================== |--------| ====================
// ==================== | screen | ====================
// ==================== |--------| ====================


/**
 * Moves the coordinates so that the size completely fits inside an existing screen
 * @param {int[]} location The coordinates [top, left]
 * @param {Dimension} size The rectangle size, an object which has two properties: width and height
 * @return {int[]} the new location
 */ 
DuScriptUI.moveInsideScreen = function (location, size)
{
    var screen = DuScriptUI.getClosestScreen(location);

    var result = location;
    var left = location[0];
    var right = location[0] + size.width;
    var top = location[1];
    var bottom = location[1] + size.height;
    // if outside on the left
    if (screen.left > location[0]) result[0] += ( screen.left - left );
    if (screen.right < right) result[0] += ( screen.right - right );
    if (screen.top > top) result[1] += ( screen.top - top );
    if (screen.bottom < bottom) result[1] += ( screen.bottom - bottom );
    return result;
}

/**
 * Gets the corners of the screen the closest to (or containing) the location
 * @param {int[]} location The coordinates
 * @return {Object} The screen object with a top, left, right and bottom property. 
 */
DuScriptUI.getClosestScreen = function(location)
{
    var screen = null;
    var distance = 999999999;
    for (var i = 0, n = $.screens.length; i < n; i++)
    {
        var s = $.screens[i];

        // check if the location is inside the screen
        if (location[0] > s.left && location[0] < s.right && location[1] > s.top && location[1] < s.bottom)
        {
            return s;
        }

        //get the center
        var c = [ (s.left + s.right)/2, (s.top + s.bottom) / 2];
        //get the distance
        var d = DuMath.length(c, location);
        if (d < distance)
        {
            screen = s;
            distance = d;
        }
    }
    return screen;
}// ==================== |----------| ====================
// ==================== | selector | ====================
// ==================== |----------| ====================


/**
 * @class
 * @name DuSelector
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A drop down selector.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.selector} to create a Selector.<br />
 * The Selector inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {string} image - The path to the current image (or a PNG as a string representation)
 * @property {Image} icon - The Image currently displayed
 * @property {StaticText} label - The current text
 * @property {string[][]} items  - The buttons, each one is an array with [text, image, helptip]
 * @property {int} index  - The current index
 * @property {string} text  - The current text
 * @property {*} currentData  - The current data
 * @property {Selector~onChange} onChange  - The function to execute when the index changes.<br />
 * You can set your own function here, which must take no argument.<br />
 * The method is called after the index has changed.
 * @property {Selector~onRefresh} onRefresh  - The function to execute to refresh the content.<br />
 * You can set your own function here, which must take no argument.
 * @category DuScriptUI
 */

/**
 * The function to execute when the index changes.<br />
 * The method is called after the index has changed.
 * @callback Selector~onChange
 * @memberof DuSelector
 */

/**
 * The function to execute to refresh the content.
 * @callback Selector~onRefresh
 * @memberof DuSelector
 */

/**
 * Adds a new button to the selector
 * @method
 * @memberof DuSelector
 * @name addButton
 * @param {string} [text] - The text displayed by the button
 * @param {string} [image] - The icon, either a path to the file or a PNG represented as a string.
 * @param {string} [helpTip] - The help tip for the entry
 * @param {*} [data] - Some data to associate with the button.
 */

/**
 * Removes all buttons from the selector. This is the same as {@link Selector.clear}.
 * @method
 * @memberof DuSelector
 * @name removeAll
 */

/**
 * Removes all buttons from the selector. This is the same as {@link Selector.removeAll}.
 * @method
 * @memberof DuSelector
 * @name clear
 */

/**
 * Changes the selection and the current index of the selector
 * @method
 * @memberof DuSelector
 * @name setCurrentIndex
 * @param {int} index - The new index
 */

/**
 * Changes the selection and the current index of the selector, using the text of the selection
 * @method
 * @memberof DuSelector
 * @name setCurrentText
 * @param {string} text - The text to select
 * @param {Boolean} [quiet=false] When true, the onChange() callback will not be triggered.
 */

/**
 * Creates a drop down selector, using image buttons
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the selector.
 * @param {string} [helpTip] - The help tip to show on the selector
 * @return {DuSelector} - The selector
 */
DuScriptUI.selector = function( container, helpTip, iconOnly )
{
    helpTip = def(helpTip, '');
    iconOnly = def(iconOnly, false);

    //create main group
    var selector = container.add( 'group' );
    selector.orientation = 'row';
    selector.spacing = 0;
    selector.alignment = [ 'fill', 'top' ];
    selector.alignChildren = [ 'center', 'center' ];
    selector.freeze = false;

    selector.mainGroup = selector.add( 'group' );
    selector.mainGroup.orientation = 'row';
    if (!iconOnly) selector.mainGroup.margins = 1;
    selector.mainGroup.spacing = 4;
    selector.mainGroup.alignment = [ 'fill', 'fill' ];
    selector.mainGroup.alignChildren = [ 'center', 'center' ];
    selector.helpTip = helpTip;
    DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.OBSIDIAN );

    //the menu button
    if (!iconOnly) {
        selector.menuButton = selector.mainGroup.add( 'image', undefined, DuScriptUI.Icon.MENU.binAsString );
        selector.menuButton.alignment = [ 'left', 'center' ];
        selector.menuButton.helpTip = helpTip;
    }
    
    //the image
    var iconGroup = selector.mainGroup.add('group');
    if (!iconOnly) {
        iconGroup.minimumSize = [20,20];
        iconGroup.margins = 2;
    }
    else {
        iconGroup.minimumSize = [16,16];
    }
    iconGroup.alignment = ['left', 'fill'];
    selector.icon = iconGroup.add( 'image', undefined, DuScriptUI.Icon.PLACEHOLDER );
    selector.icon.alignment = [ 'center', 'center' ];
    selector.icon.helpTip = helpTip;

    if (!iconOnly) {
        //add fillers to be able to click anywhere on the button
        selector.fillerM = selector.mainGroup.add( 'statictext', undefined, " " );
        selector.fillerM.alignment = [ 'left', 'fill' ];
        selector.fillerM.size = [ 0, -1];

        //the text
        selector.label = selector.mainGroup.add( 'statictext', undefined, '' );
        selector.label.helpTip = '';
        selector.label.alignment = [ 'fill', 'center' ];
        selector.label.helpTip = helpTip;

        //add fillers to be able to click anywhere on the button
        selector.fillerR = selector.mainGroup.add( 'statictext', undefined, " " );
        selector.fillerR.alignment = [ 'fill', 'fill' ];
    }
    

    //create popup
    selector.popup = new Window( 'palette', '', undefined,
    {
        borderless: true
    } );
    selector.popup.margins = 2;
    selector.popup.spacing = 0;
    selector.popup.closeButton = DuScriptUI.button( selector.popup, i18n._("Cancel") );
    selector.popup.closeButton.onClick = selector.popup.onDeactivate = function()
    {
        selector.popup.hide();
        DuScriptUI.eventFunctionsPaused = false;
    };
    selector.popup.buttons = selector.popup.add( 'group' );
    selector.popup.buttons.orientation = 'column';
    selector.popup.buttons.margins = 0;
    selector.popup.buttons.spacing = 0;
    selector.popup.hide();

    //add default button
    selector.items = [];
    selector.index = -1;
    selector.currentData = null;

    selector.addButton = function( text, image, helpTip, data )
    {
        selector.popup.hide();

        var options = {};
        text = def(text, '-');
        if (jstype(text) != 'string') options = text;
        options.text = def(options.text, text);
        
        image = def(image, '');
        options.image = def(options.image, image);

        helpTip = def(helpTip, '');
        options.helpTip = def(options.helpTip, helpTip);

        data = def(data, null);
        options.data = def(options.data, data);
        
        if (!iconOnly) {
            var size = text.length * 7;
            if ( selector.label.minimumSize.width < size ) selector.label.minimumSize.width = size;
        }

        var index = selector.items.length;
        selector.items.push( [ options.text, options.image, options.helpTip, options.data ] );
        //add to popup
        options.localize = false;
        options.ignoreUIMode = true;
        var button = DuScriptUI.button( selector.popup.buttons, options );
        button.bgColor = DuColor.Color.OBSIDIAN;
        button.dim();
        button.onClick = function()
        {
            selector.setCurrentIndex( index );
            selector.popup.hide();
        };
        selector.popup.layout.layout(true);
        selector.popup.update();
    }

    selector.removeAll = function()
    {
        selector.popup.hide();
        selector.items = [];
        selector.setCurrentIndex( 0 );
        for ( var i = selector.popup.buttons.children.length - 1; i >= 0; i-- )
        {
            selector.popup.buttons.remove( selector.popup.buttons.children[ i ] );
        }
        selector.popup.layout.layout(true);
        selector.popup.update();
    }
    selector.clear = selector.removeAll;

    selector.setCurrentIndex = function( index, quiet )
    {
        if ( index < 0 ) return;
        if ( index >= selector.items.length ) return;

        var item = selector.items[ index ];

        if ( !item ) return;

        var image = item[ 1 ];
        if ( image != '' )
        {
            if (image instanceof DuBinary) image = image.toFile();
            selector.icon.show();
            selector.icon.image = image;
        }
        else if (!iconOnly)
        {
            selector.icon.hide();
        }
        if (!iconOnly) selector.label.text = item[ 0 ];

        selector.index = index;

        selector.text = item[ 0 ];

        selector.currentData = item[3];

        if (!quiet && !selector.freeze) selector.onChange();
    }

    selector.setCurrentText = function( text )
    {
        for (var i = 0, n = selector.items.length; i < n; i++)
        {
            if (selector.items[i][0] == text)
            {
                selector.setCurrentIndex(i);
                break;
            }
        }
    }

    selector.clicked = function( e )
    {
        selector.onRefresh();
        var popup = false;

        if (!iconOnly) if ( e.target === selector.menuButton ) popup = true;
        if ( e.altKey ) popup = true;
        

        if ( popup )
        {
            var x = e.screenX - e.clientX;
            var y = e.screenY - e.clientY;
            selector.popup.location = [ x, y ];
            selector.popup.show();
            DuScriptUI.eventFunctionsPaused = true;
        }
        else
        {
            var index = selector.index;
            if ( e.ctrlKey ) index = -1;
            if ( index == selector.items.length - 1 ) index = 0;
            else index++;
            selector.setCurrentIndex( index );
            DuScriptUI.eventFunctionsPaused = false;
        }
    }

    //mouse over
    selector.highlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();
        DuScriptUI.setBackgroundColor( selector, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
        DuScriptUI.highlightedControls.push( selector );
    }

    selector.dim = function( e )
    {
        DuScriptUI.setBackgroundColor( selector, DuColor.Color.TRANSPARENT );
        //if (!iconOnly) DuScriptUI.setTextColor( selector.label, DuColor.Color.APP_TEXT_COLOR );
    }

    selector.mainGroup.addEventListener( "mouseover", selector.highlight );
    selector.mainGroup.addEventListener( "click", selector.clicked, true );

    selector.onChange = function() {};
    selector.onRefresh = function (){};

    selector.dim();

    return selector;
}

// ==================== |------| ====================
// ==================== | text | ====================
// ==================== |------| ====================


/**
 * @class
 * @name DuEditText
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A Nice EditText.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.editText} to create a Nice EditText.<br />
 * The Nice EditText inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {string} text - Read-Only | The text displayed
 * @property {DuColor} textColor - The color of the text
 * @property {DuEditText~onActivate} onActivate - Function to execute when activated
 * @property {DuEditText~onDeactivate} onDeactivate - Function to execute when deactivated
 * @property {DuEditText~onChange} onChange - Function to execute when text changed
 * @property {DuEditText~onChange} onEnterPressed - Function to execute when enter key is pressed whil in edit mode
 * @category DuScriptUI
 */

/**
 * The function to execute when the text is changed.
 * @callback DuEditText~onChange
 * @memberof DuEditText
 */

/**
 * Function to execute when enter key is pressed whil in edit mode.
 * @callback DuEditText~onEnterPressed
 * @memberof DuEditText
 */


/**
 * The function to execute when the box is activated.
 * @callback DuEditText~onActivate
 * @memberof DuEditText
 */

/**
 * The function to execute when the box is deactivated.
 * @callback DuEditText~onDeactivate
 * @memberof DuEditText
 */

/**
 * Changes the text
 * @method
 * @memberof DuEditText
 * @name setText
 * @param {string} text - The new text
 */

 /**
 * Changes the prefix
 * @method
 * @memberof DuEditText
 * @name setPrefix
 * @param {string} prefix - The new prefix
 */

 /**
 * Changes the suffix
 * @method
 * @memberof DuEditText
 * @name setSuffix
 * @param {string} suffix - The new suffix
 */

/**
 * Changes the placeholder
 * @method
 * @memberof DuEditText
 * @name setPlaceholder
 * @param {string} placeholder - The placeholder text
 */

/**
 * Creates a nice edittext where the edit text is shown only on click.
 * @param {Window|Panel|Group}	container		- The ScriptUI Object which will contain and display the nice edit text.
 * @param {string} text - The initial text in the edit.
 * @param {string} [prefix=""] - A text prefix to display.
 * @param {string} [suffix=""] - A text suffix to display.
 * @param {string} [placeHolder=""] - A place holder default text.
 * @param {string} [helpTip=""] The helpTip of this control
 * @param {bool} [localize=true] Whether to translate the texts of this control
 * @return {DuEditText}	The custom Group containing the edit text.
 */
DuScriptUI.editText = function( container, text, prefix, suffix, placeHolder, helpTip )
{
    var options = {};
    text = def(text, '');

    if (jstype(text) != 'string') options = text;

    prefix = def(prefix, '');
    suffix = def(suffix, '');
    placeHolder = def(placeHolder,'');
    helpTip = def(helpTip, '');

    options.text = def(options.text, text);
    options.suffix = def(options.suffix, suffix);
    options.prefix = def(options.prefix, prefix);
    options.placeHolder = def(options.placeHolder, placeHolder);
    options.helpTip = def(options.text, helpTip);

    var niceEditText = container.add( 'group' );
    niceEditText.orientation = 'stack';
    niceEditText.margins = 0;
    niceEditText.alignChildren = [ 'fill', 'fill' ];
    niceEditText.placeHolder = options.placeHolder;
    var staticText = options.text;
    if ( options.text == "" && options.placeHolder != "" ) staticText = options.placeHolder;
    niceEditText.static = niceEditText.add( 'statictext', undefined, options.prefix + staticText + options.suffix );
    niceEditText.static.alignment = [ 'fill', 'center' ];
    niceEditText.static.helpTip = options.helpTip;
    if ( options.text == "" && options.placeHolder != "" ) DuScriptUI.setTextColor( niceEditText.static, DuColor.Color.APP_HIGHLIGHT_COLOR.darker(150) );
    else DuScriptUI.setTextColor( niceEditText.static, DuColor.Color.APP_HIGHLIGHT_COLOR );
    niceEditText.edit = niceEditText.add( 'edittext', undefined, options.text );
    niceEditText.edit.visible = false;
    niceEditText.edit.helpTip = options.helpTip;
    niceEditText.edit.alignment = ['fill', 'center'];
    niceEditText.prefix = options.prefix;
    niceEditText.suffix = options.suffix;
    niceEditText.textColor = DuColor.Color.APP_HIGHLIGHT_COLOR;
    niceEditText.freeze = false;

    niceEditText.text = options.text;
    niceEditText.tempText = '';
    niceEditText.previousText = options.text;
    niceEditText.editing = false;

    niceEditText.onActivate = function() {};
    niceEditText.onDeactivate = function() {};
    niceEditText.onChange = function() {};
    niceEditText.onChanging = function() {};
    niceEditText.onEnterPressed = function() {};

    niceEditText.clicked = function()
    {
        if ( niceEditText.editing ) return;
        else
        {
            niceEditText.static.visible = false;
            niceEditText.edit.visible = true;
            niceEditText.editing = true;
            niceEditText.edit.active = true;
        }
    }

    niceEditText.changed = function()
    {
        if ( niceEditText.text ==  niceEditText.edit.text ) 

        var freeze = niceEditText.freeze;
        niceEditText.freeze = true;

        niceEditText.editing = false;
        var staticText = '';
        if ( niceEditText.edit.text == '' && niceEditText.placeHolder != '' )
        {
            DuScriptUI.setTextColor( niceEditText.static, niceEditText.textColor.darker(200) );
            staticText = niceEditText.placeHolder;
        }
        else
        {
            if ( niceEditText.placeHolder != '' ) DuScriptUI.setTextColor( niceEditText.static, niceEditText.textColor );
            staticText = niceEditText.edit.text;
        }
        niceEditText.static.text = niceEditText.prefix + staticText + niceEditText.suffix;

        niceEditText.previousText = niceEditText.text;

        niceEditText.text = niceEditText.edit.text;
        niceEditText.edit.visible = false;
        niceEditText.static.visible = true;
        niceEditText.edit.active = false;

        niceEditText.freeze = freeze;
        if (!niceEditText.freeze) niceEditText.onChange();
    }

    niceEditText.changing = function()
    {
        var freeze = niceEditText.freeze;
        niceEditText.freeze = true;

        niceEditText.tempText = niceEditText.edit.text;

        niceEditText.freeze = freeze;
        // workaround for ae not sending the event
        if (!niceEditText.freeze) niceEditText.onChanging();
    }

    niceEditText.enterPressed = function()
    {
        if (!niceEditText.freeze) niceEditText.onEnterPressed();
    }

    niceEditText.setText = function( text )
    {
        var freeze = niceEditText.freeze;
        niceEditText.freeze = true;

        niceEditText.previousText = niceEditText.text;
        if (text == '')
        {
            niceEditText.edit.text = niceEditText.text = text;
            niceEditText.static.text = niceEditText.prefix + niceEditText.placeHolder + niceEditText.suffix;
        }
        else 
        {
            niceEditText.edit.text = niceEditText.text = text;
            niceEditText.static.text = niceEditText.prefix + text + niceEditText.suffix;
        }

        niceEditText.freeze = freeze;
    }

    niceEditText.setPrefix = function( prefix )
    {
        niceEditText.prefix = prefix;
        niceEditText.setText(niceEditText.text);
    }

    niceEditText.setSuffix = function( suffix )
    {
        niceEditText.suffix = suffix;
        niceEditText.setText(niceEditText.text);
    }

    niceEditText.setPlaceholder = function( ph )
    {
        var freeze = niceEditText.freeze;
        niceEditText.freeze = true;

        niceEditText.placeHolder = ph;
        var text = niceEditText.text;
        if (text == '')
        {
            niceEditText.edit.text = niceEditText.text = text;
            niceEditText.static.text = niceEditText.prefix + niceEditText.placeHolder + niceEditText.suffix;
        }
        else 
        {
            niceEditText.edit.text = niceEditText.text = text;
            niceEditText.static.text = niceEditText.prefix + text + niceEditText.suffix;
        }

        niceEditText.freeze = freeze;
    }

    niceEditText.edit.onActivate = function()
    {
        if (!niceEditText.freeze) niceEditText.onActivate();
    }
    niceEditText.edit.onDeactivate = function()
    {
        if (!niceEditText.freeze) niceEditText.changed();
        if (!niceEditText.freeze) niceEditText.onDeactivate();
    }
    niceEditText.edit.onChange = niceEditText.changed;
    niceEditText.edit.onChanging = niceEditText.changing;
    niceEditText.addEventListener( "mousedown", niceEditText.clicked, true );
    niceEditText.addEventListener( "mouseover", DuScriptUI.dimControls );
    niceEditText.edit.addEventListener('keydown', function(e){ if (e.keyName == 'Enter' && !niceEditText.freeze) niceEditText.enterPressed(); } );

    niceEditText.helpTip = options.helpTip;

    return niceEditText;
}

/**
 * Creates a statictext (with an optionnal color).
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the nice edit text.
 * @param {string} text - The initial text in the edit.
 * @param {DuColor} [color] - The color of the text. By default, uses a slightly darker text than the Host App text color
 * @param {bool} [localize=true] - Set this to false to never translate this text.
 * @param {bool} [multiLine=false] - Set this to true to add a multiline text. Auto detected by default if the text contains the newline character.
 * @return {StaticText} The ScriptUI StaticText created.
 */
DuScriptUI.staticText = function( container, text, color, multiLine )
{
    var options = {};
    text = def(text, '');

    if (jstype(text) != 'string') options = text;

    options.text = def(options.text, text);

    multiLine = def(multiLine, options.text.indexOf("\n") >= 0 )
    color = def(color, DuColor.Color.APP_TEXT_COLOR.darker(130) );

    options.color = def(options.color, color);
    options.multiLine = def(options.multiLine, multiLine);

    var t = container.add( 'statictext', undefined, options.text,
    {
        multiline: options.multiLine
    } );

    DuScriptUI.setTextColor( t, options.color );

    t.setText = function (txt) { t.text = txt; };

    return t;
}
// ==================== |--------------| ====================
// ==================== | fileSelector | ====================
// ==================== |--------------| ====================


/**
 * @class
 * @name DuFolderSelector
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A "Browse" button for folders only, with an optional text field for the path.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.folderSelector} to create a selector.<br />
 * The DuFolderSelector inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {string} path - The folder path, initialized with an empty string. Note that this may not point to an existing folder if the user entered an incorrect path in the text field.<br />
 * To be sure to get an existing folder, you can use the getFolder() method.
 * @property {DuEditText|null} editText - The text field, if any.
 * @property {DuButton} button - The "Browse" button.
 * @category DuScriptUI
 */

/**
 * Gets the selected folder.
 * @method
 * @memberof DuFolderSelector
 * @name getFolder
 * @return {Folder|null} The new Folder, if any. null if the Folder does not exist or the user has input an incorrect path.
 */

/**
 * Creates a folder selector with a field for the path and a browse button<br />
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the panel.
 * @param {string} [text="Browse..."] - The text to display on the button.
 * @param {boolean} [textField=true] - Whether to show the text field for the path.
 * @param {string} [helpTip=''] - The helptip for this control.
 * @param {string} [orientation='row'] - The orientation of the control (button+edittext).
 * @return {DuFolderSelector} The control.
 */
DuScriptUI.folderSelector = function ( container, text, textField, helpTip, orientation )
{
    text = def(text, "Browse...");
    textField = def(textField, true);
    helpTip = def(helpTip, '');
    orientation = def(orientation, 'row');

    if (DuESF.scriptSettings.get("common/uiMode", 0) >= 1) orientation = 'row';

    var folderSelector = container.add('group');
    folderSelector.orientation = orientation;
    folderSelector.margins = 0;
    folderSelector.spacing = 0;

    folderSelector.path = '';

    folderSelector.button = DuScriptUI.button(
        folderSelector,
        text,
        w12_folder,
        helpTip
        );
    if (orientation == 'row') folderSelector.button.alignment = ['left','fill'];

    folderSelector.editText = null;
    if (textField) {
        folderSelector.editText = DuScriptUI.editText(
            folderSelector,
            '',
            '',
            '',
            i18n._p("file system", "Path..."), /// TRANSLATORS: path for a file or a folder
            helpTip
            );
        folderSelector.editText.alignment = ['fill','center'];

        folderSelector.editText.onChange = function () {
            folderSelector.path = folderSelector.editText.text;
        }
    }

    folderSelector.button.onClick = function () {
        var folder = Folder.selectDialog ("Select Folder");
        if (!folder) return;
        folderSelector.editText.setText(folder.fsName);
        folderSelector.path = folder.absoluteURI;
    }

    folderSelector.getFolder = function () {
        var folder = new Folder(folderSelector.path);
        if (folder.exists) return folder;
        else return null;
    }

    return folderSelector;
}


/**
 * @class
 * @name DuFileSelector
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A "Browse" button for files only, with an optional text field for the path.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.fileSelector} to create a selector.<br />
 * The DuFileSelector inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {string} path - The file path, initialized with an empty string. Note that this may not point to an existing folder if the user entered an incorrect path in the text field.<br />
 * To be sure to get an existing file, you can use the getFile() method.
 * @property {DuEditText|null} editText - The text field, if any.
 * @property {DuButton} button - The "Browse" button.
 * @property {DuFileSelector~onChange} onChange A function called when the file has changed.
 * @category DuScriptUI
 */

/**
 * Called when the file has changed.
 * @callback onChange
 * @memberof DuFileSelector
 */

/**
 * Gets the selected file.
 * @method
 * @memberof DuFileSelector
 * @name getFile
 * @return {File|null} The new File, if any. null if the File does not exist or the user has input an incorrect path.
 */

/**
 * Creates a file selector with a field for the path and a browse button.
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the panel.
 * @param {string} [text="Browse..."] - The text to display on the button.
 * @param {boolean} [textField=true] - Whether to show the text field for the path.
 * @param {string} [helpTip=''] - The helptip for this control.
 * @param {string|DuBinary} [image] - The image to use as an icon; a "file" icon by default.
 * @param {string} [mode='open'] - The open mode, either 'open' or 'save'.
 * @param {string} [orientation='row'] - The orientation of the control (button+edittext).
 * @return {DuFileSelector} The control.
 */
DuScriptUI.fileSelector = function ( container, text, textField, helpTip, image, mode, filters, orientation )
{
    text = def(text, "Browse...");
    textField = def(textField, true);
    helpTip = def(helpTip, '');
    mode = def(mode, 'open');
    image = def(image, w12_file);
    filters = def(filters, "All files: *.*");
    orientation = def(orientation, uiMode >= 1 ? 'row' : 'column');

    var uiMode = DuESF.scriptSettings.get("common/uiMode", 0);

    var fileSelector = container.add('group');
    fileSelector.orientation = orientation;
    fileSelector.margins = 0;
    fileSelector.spacing = 0;

    fileSelector.path = '';

    fileSelector.button = DuScriptUI.button(
        fileSelector,
        text,
        image,
        helpTip
        );
    if (orientation == 'row') fileSelector.button.alignment = ['left','fill'];

    fileSelector.editText = null;
    if (textField) {
        fileSelector.editText = DuScriptUI.editText(
            fileSelector,
            '',
            '',
            '',
            i18n._p("file system", "Path..."), /// TRANSLATORS: path for a file or a folder
            helpTip
            );
        fileSelector.editText.alignment = ['fill','center'];

        fileSelector.editText.onChange = function () {
            fileSelector.path = fileSelector.editText.text;
            fileSelector.onChange();
        }
    }

    fileSelector.onChange = function() {};

    fileSelector.button.onClick = function () {
        var file = new File(fileSelector.path);
        if (file.exists)
        {
            if (mode == 'open') file = file.openDlg("Select File", filters);
            else file = file.saveDlg ("Save File", filters);
        }
        else 
        {
            if (mode == 'open') file = File.openDialog("Select File",filters);
            else file = File.saveDialog ("Save File", filters);
        }
        
        if (!file) return;

        if (mode == 'open' && !file.exists)
        {
            if (fileSelector.editText) fileSelector.editText.setText("");
            fileSelector.path = file.absoluteURI;
        }
        else
        {
            if (fileSelector.editText) fileSelector.editText.setText(file.fsName);
            fileSelector.path = file.absoluteURI;
        }

        fileSelector.onChange();
    }

    fileSelector.getFile = function () {
        var file = new File(fileSelector.path);
        if (!file.exists && mode == 'open') return null;
        else return file;
    }

    fileSelector.setPath = function(path)
    {
        var file = new File(path);
        fileSelector.path = file.absoluteURI;
        if ((mode == 'open' && file.exists) || mode == 'save')
        {
            fileSelector.editText.setText( file.fsName ); 
        }
        else
        {
            fileSelector.editText.setText( "" );
        }
    }

    fileSelector.setPlaceholder = function(text)
    {
        fileSelector.editText.setPlaceholder(text);
    }



    return fileSelector;
}// ==================== |--------| ====================
// ==================== | slider | ====================
// ==================== |--------| ====================


/**
 * @class
 * @name DuSlider
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A Nice Slider.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.slider} to create a Nice Slider.<br />
 * The Nice Slider inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {int} value - Read-Only | The current value
 * @property {DuSlider~onChanging} onChanging - Function to execute when changing
 * @property {DuSlider~onChange} onChange - Function to execute when changed
 * @category DuScriptUI
 */

/**
 * Function to execute when changing
 * @callback DuSlider~onChanging
 * @memberof DuSlider
 */

/**
 * Function to execute when changed
 * @callback DuSlider~onChange
 * @memberof DuSlider
 */

/**
 * Changes the value
 * @method
 * @memberof DuSlider
 * @name setValue
 * @param {int} value - The new value
 */
 
/**
 * Creates a slider.
 * @param {Window|Panel|Group}	container		- The ScriptUI Object which will contain and display the nice edit text.
 * @param {int}	[defaultValue=0]		- The initial value.
 * @param {int}	[min=0]		- The minimal value.
 * @param {int}	[max=100]		- The maximal value.
 * @param {string}	[orientation='column']		- Either 'row' or 'column'
 * @param {boolean}	[invertedAppearance]		- Revert the slider with max value on the left
 * @param {string}	[prefix]		- A text prefix to display.
 * @param {string}	[suffix]		- A text suffix to display.
 * @param {string}	[textAlignment='center'] - The alignment of the text.<br />
 * One of 'left', 'center', 'right' for column orientation,<br />
 * And 'top', 'center', 'bottom' for row orientation.
 * @param {int[]}	[valueButtons=[]] - A list of predefined values to add as buttons.
 * @return {DuSlider}	The custom Group containing the slider.
 * @todo implement helpTip
 */
DuScriptUI.slider = function( container, defaultValue, min, max, orientation, invertedAppearance, prefix, suffix, textAlignment, valueButtons )
{
    prefix = def(prefix, '');
    suffix = def(suffix, '');
    min = def( min, 0);
    max = def( max, 100);
    defaultValue = def(defaultValue, 0);
    textAlignment = def(textAlignment, 'center');
    valueButtons = def(valueButtons, []);
    orientation = def(orientation, 'column');

    if ( valueButtons.length > 0 && textAlignment == 'center' )
    {
        if ( orientation == 'column' ) textAlignment = 'right';
        else textAlignment = 'bottom';
    }

    var niceSlider = container.add( 'group' );
    niceSlider.orientation = orientation;
    niceSlider.spacing = 0;
    niceSlider.margins = 0;
    niceSlider.alignment = [ 'fill', 'top' ];
    niceSlider.alignChildren = [ 'fill', 'fill' ];

    niceSlider.value = defaultValue;
    niceSlider.invertedAppearance = invertedAppearance;

    var sliderValue = defaultValue;
    if ( niceSlider.invertedAppearance ) sliderValue = max - defaultValue + min;

    niceSlider.slider = niceSlider.add( 'slider', undefined, sliderValue, min, max );

    niceSlider.editGroup = DuScriptUI.group( niceSlider );
    niceSlider.editGroup.spacing = 0;
    niceSlider.editGroup.margins = 0;

    //a function to add buttons before or after the edittext, depending on the textAlignment
    niceSlider.buttons = [];

    function addButtons ()
    {
        //add
        for ( var i = 0, num = valueButtons.length; i < num; i++ )
        {
            niceSlider.buttons[i] = DuScriptUI.smallbutton(
                niceSlider.editGroup,
                valueButtons[ i ].toString(),
                "",
                valueButtons[ i ]
                );
            niceSlider.buttons[i].alignment = [ 'fill', 'fill' ];
            niceSlider.buttons[i].onClick = function( val )
            {
                niceSlider.setValue( val );
                niceSlider.onChange();
            }
        }
    }

    if ( textAlignment == 'right' || textAlignment == 'bottom' ) addButtons();

    niceSlider.edit = DuScriptUI.editText( niceSlider.editGroup, defaultValue.toString(), prefix + ': ', suffix + ' ', '', '', false );
    var numChars = max.toString().length;
    if ( min.toString().length > max.toString().length ) numChars = min.toString().length;
    niceSlider.edit.edit.characters = numChars + prefix.length + suffix.length - 1;
    if ( orientation == 'row' )
    {
        niceSlider.editGroup.alignment = [ 'right', 'fill'];
        niceSlider.edit.alignment = [ 'center', textAlignment ];
        niceSlider.edit.static.justify = 'center';
    }
    else
    {
        niceSlider.editGroup.alignment = [ 'fill', 'bottom'];
        niceSlider.edit.alignment = [ textAlignment, 'center' ];
        niceSlider.edit.static.justify = textAlignment;
    }

    if ( textAlignment == 'left' || textAlignment == 'top' ) addButtons();

    niceSlider.onChanging = function() {};
    niceSlider.onChange = function() {};

    niceSlider.setValue = function( val )
    {
        niceSlider.value = val;
        niceSlider.edit.setText( niceSlider.value );
        if ( niceSlider.invertedAppearance ) niceSlider.slider.value = niceSlider.slider.maxvalue - val + niceSlider.slider.minvalue;
        else niceSlider.slider.value = val;
    }

    //events
    niceSlider.slider.onChanging = function()
    {
        if ( niceSlider.invertedAppearance ) niceSlider.value = Math.round( niceSlider.slider.maxvalue - niceSlider.slider.value + niceSlider.slider.minvalue );
        else niceSlider.value = Math.round( niceSlider.slider.value );
        niceSlider.edit.setText( niceSlider.value );
        niceSlider.onChanging();
    }

    niceSlider.slider.onChange = function()
    {
        niceSlider.onChange();
    }

    niceSlider.edit.onChanging = function()
    {
        var val = parseInt( niceSlider.edit.text );
        if ( isNaN( val ) ) return;
        niceSlider.value = val;
        if ( niceSlider.invertedAppearance ) niceSlider.slider.value = niceSlider.slider.maxvalue - val + niceSlider.slider.minvalue;
        else niceSlider.slider.value = val;
        niceSlider.onChanging();
    }
    niceSlider.edit.onChange = function()
    {
        var val = parseInt( niceSlider.edit.text );
        if ( isNaN( val ) ) return;
        if ( val < niceSlider.slider.minvalue ) val = niceSlider.slider.minvalue;
        if ( val > niceSlider.slider.maxvalue ) val = niceSlider.slider.maxvalue;
        niceSlider.edit.setText( val );
        niceSlider.value = val;
        if ( niceSlider.invertedAppearance ) niceSlider.slider.value = niceSlider.slider.maxvalue - val + niceSlider.slider.minvalue;
        else niceSlider.slider.value = val;
        niceSlider.onChange();
    }

    niceSlider.addEventListener( "mouseover", DuScriptUI.dimControls );

    return niceSlider;
}
// ==================== |------| ====================
// ==================== | form | ====================
// ==================== |------| ====================


/**
 * @class
 * @name DuForm
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A Form.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.form} to create a Form.<br />
 * The DuForm inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {Group} labels - The left vertical group
 * @property {Group} buttons - The right vertical group
 * @category DuScriptUI
 */

/**
 * Adds a field to the form
 * example: form.addField('Composition:','dropdownlist',['Composition1','Composition2'],'Select a composition')
 * @method
 * @memberof DuForm
 * @name addField
 * @param {string}			label		- The label text.
 * @param {string}			type		- The type of ScriptUI object to add (like 'button','edittext', etc.).
 * @param {object}			[value]		- The default value or content of the field added, depends on the type.
 * @param {string}			[helpTip]		- The helpTip of the form control.
 * @return {ScriptUI[]}		An array with at 0 the StaticText label, and at 1 the ScriptUI object of the type type, added to the form
 */

/**
 * Creates a layout to add forms to a UI, using ScriptUI groups.<br />
 * You can easily add controls/fields to this form using DuScriptUI.addField
 * @param {Window|Panel|Group}	container		- The ScriptUI Object which will contain and display the form.
 * @return {DuForm}		The custom Group containing the form.
 */
DuScriptUI.form = function( container )
{
    var mainGroup = container.add( 'group' );
    mainGroup.orientation = 'row';
    mainGroup.margins = 0;
    mainGroup.spacing = DuScriptUI.defaultSpacing;

    var labelsGroup = mainGroup.add( 'group' );
    labelsGroup.alignment = [ 'left', 'top' ];
    labelsGroup.orientation = 'column';
    labelsGroup.alignChildren = [ 'left', 'bottom' ];
    labelsGroup.spacing = DuScriptUI.defaultSpacing;

    var buttonsGroup = mainGroup.add( 'group' );
    buttonsGroup.alignment = [ 'fill', 'top' ];
    buttonsGroup.orientation = 'column';
    buttonsGroup.alignChildren = [ 'fill', 'fill' ];
    buttonsGroup.spacing = DuScriptUI.defaultSpacing;

    mainGroup.labels = labelsGroup;
    mainGroup.buttons = buttonsGroup;

    mainGroup.addEventListener( "mouseover", DuScriptUI.dimControls );

    mainGroup.addField = function( label, type, value, helpTip )
    {
        helpTip = def (helpTip, '');

        var control = null;
        var height = 20;
        if ( type == "DuButton" )
        {
            control = DuScriptUI.button( mainGroup.buttons, '', value[ 0 ], helpTip );
            height = control.image.preferredSize[ 1 ];
        }
        else
        {
            control = mainGroup.buttons.add( type, undefined, value );
            control.helpTip = helpTip;
            height = control.preferredSize[ 1 ];
        }

        var l = mainGroup.labels.add( 'statictext', undefined, label );
        l.helpTip = helpTip;

        l.minimumSize.height = l.maximumSize.height = height;
        return [ l, control ];
    }

    return mainGroup;
}
// ==================== |----------| ====================
// ==================== | tabPanel | ====================
// ==================== |----------| ====================

/**
 * @class
 * @name DuTabPanel
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A Panel with tabs.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.tabPanel} to create a picker.<br />
 * The DuTabPanel inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {DuButton[]} buttons - The buttons for the tabs. Not that an "index" property is added to the button, containing the DuTab index.
 * @property {DuTab[]} tabs - The tabs.
 * @property {int} index - The currently visible tab.
 * @property {Group} buttonsGroup - The ScriptUI Group containing the buttons
 * @property {Group} mainGroup - The ScriptUI Group containing the tabs
 * @property {string} scriptUIPanel - The file name ("script.jsx") of a scriptUI Panel. Alt+Click on this tab will open/close this panel.
 * @property {DuTabPanel~onChange} onChange - Called when the index changes.
 * @category DuScriptUI
 */

/**
 * Adds a new empty DuTab in the DuTabPanel.
 * @method
 * @memberof DuTabPanel
 * @name addTab
 * @param {string} [text=''] - The label of the button.
 * @param {string} [image=''] - The path to the icon.
 * @param {string} [helpTip=''] - The helptip.
 * @param {bool} [translatable=true] - Set to false to not translate this tab text and helptip
 * @return {DuTab} The new DuTab.
 */

/**
 * Sets the current visible tab.
 * @method
 * @memberof DuTabPanel
 * @name setCurrentIndex
 * @param {int} [index=0] - The index of the tab to show.
 */

/**
 * Called when the index changes.
 * @callback DuTabPanel~onChange
 * @memberof DuTabPanel
 */

/**
 * @class
 * @name DuTab
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A DuTab inside a {@link DuTabPanel}.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuTabPanel.add} to create a new DuTab.
 * @property {int} index - The index of this tab.
 * @property {DuButton} button - The button associated with this tab.
 * @property {DuTab~tabActivated} onActivate - The function to execute when the tab is displayed.
 * @property {DuTab~tabDeActivated} onActivate - The function to execute when the tab is hidden.
 * @property {DuTab~build} build - You can use this callback to add a function which builds the UI of the tab, it will be called on first display.
 * @category DuScriptUI
 */

/**
 * The function to execute when the tab is about to be displayed.
 * @callback DuTab~tabActivated
 * @memberof DuTab
 */

/**
 * The function to execute when the tab is hidden.
 * @callback DuTab~tabDeActivated
 * @memberof DuTab
 */

/**
 * You can use this callback to add a function which builds the UI of the tab, it will be called on first display.<br />
 * This allows a faster startup of your script by delaying the creation of the tabs which are hidden at startup.<br />
 * You can add controls in the <code>this</code> object.
 * @callback DuTab~build
 * @memberof DuTab
 */

/**
 * Creates a panel with tabs
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the panel.
 * @param {string} tabOrientation - The orientation to use for tab buttons.
 * @return {DuTabPanel} The panel.
 */
DuScriptUI.tabPanel = function( container, tabOrientation )
{
    tabOrientation = def(tabOrientation, 'row');

    var panel = DuScriptUI.group( container, 'column' );

    panel.tabOrientation = tabOrientation;
    panel.alignment = [ 'fill', 'fill' ];
    panel.buttons = [];
    panel.tabs = [];
    panel.tabGroups = [];
    panel.showTitle = true;
    panel.onChange = function() {};

    //adds a group for the buttons
    panel.buttonsGroup = DuScriptUI.group( panel, 'row' );
    //adds a group for the tabs
    panel.mainGroup = DuScriptUI.group( panel, 'stack' );
    panel.mainGroup.alignment = [ 'fill', 'fill' ];

    panel.addTab = function( text, image, helpTip, translatable )
    {
        text = def( text, '');
        image = def (image, '');
        translatable = def (translatable, true);

        var uiMode = DuESF.scriptSettings.get("common/uiMode", 0);
        if (uiMode >= 2 && image != '') text = '';

        // If this button and the previous one have a label, add a spacer
        if (panel.buttons.length > 0)
        {
            var prevButton = panel.buttons[panel.buttons.length-1];
            if (prevButton.label) if (prevButton.label.text != '' && text != '')
            {
                var spacer = panel.buttonsGroup.add('group');
                spacer.spacing = 0;
                spacer.margins = 0;
                spacer.minimumSize = [5,-1];
            }
        }

        //adds the new button
        var button = DuScriptUI.checkBox( panel.buttonsGroup, text, image, helpTip, undefined, undefined, panel.tabOrientation, true );
        button.index = panel.tabs.length;       
        panel.buttons.push( button );

        //adds the new tab
        var tab = DuScriptUI.group( panel.mainGroup, 'column' );
        tab.alignment = [ 'fill', 'fill' ];
        tab.visible = false;
        tab.activated = false;

        //the onActivate callBack
        tab.tabActivated = function() {};
        tab.tabDeActivated = function() {};
        //the build callBack
        tab.build = function( theTab ) {};
        tab.duBuild = function() { 
            if ( tab.built ) return;
            DuDebug.log( "TabPanel - Building Tab: " + tab.name + ' ' + tab.index );
            tab.build( tab );
            //resize everything
            DuDebug.log( "TabPanel - Tab Built, Layout..." );
            DuScriptUI.layout( tab );

            DuDebug.log( "TabPanel - Tab ready." );
            tab.built = true;
        }
        tab.built = false;

        tab.scriptUIPanel = "";

        tab.button = button;

        //add to the list
        tab.index = panel.tabs.length;
        tab.name = text;
        panel.tabs.push( tab );

        DuScriptUI.allTabs.push( tab );

        button.onClick = function()
        {
            panel.setCurrentIndex( button.index );
        }

        return tab;
    }

    panel.setCurrentIndex = function( index )
    {
        index = def( index, panel.index );

        var numTabs = panel.tabs.length;

        if ( numTabs == 0 ) return;

        //hide all tabs
        for ( var i = 0; i < numTabs; i++ )
        {
            var tab = panel.tabs[ i ];
            tab.activated = i == index;
            // Build only AFTER main UI has been shown, this improves performance
            // DuScriptUI.showUI will build visible tabs
            if ( tab.activated && DuScriptUI.uiShown )
            {
                tab.duBuild();
                tab.visible = true;
                tab.tabActivated();
            }
            if ( !tab.activated )
            {
                tab.visible = false;
                tab.tabDeActivated();
            }

            panel.buttons[ i ].setChecked( tab.activated );
        }

        if ( index >= 0 && index < numTabs ) panel.index = index;
        else panel.index = -1;

        panel.onChange();
    }

    return panel;
}

/**
 * Builds all the tabs from all the tab panels
 */
DuScriptUI.buildAllTabs = function()
{
    for ( var i = 0, num = DuScriptUI.allTabs.length; i < num; i++ )
    {
        var tab = DuScriptUI.allTabs[ i ];
        if ( !tab.built )
        {
            tab.build( tab );
            //resize everything
            DuScriptUI.layout( tab );
            tab.built = true;
        }
    }
    //check if there's still unbuilt tabs which may have been added
    var redo = false;
    for ( var i = DuScriptUI.allTabs.length - 1; i >= 0; i-- )
    {
        var tab = DuScriptUI.allTabs[ i ];
        if ( !tab.built )
        {
            redo = true;
            break;
        }
    }
    if ( redo ) DuScriptUI.buildAllTabs();
}
// ==================== |---------------| ====================
// ==================== | colorSelector | ====================
// ==================== |---------------| ====================

/**
 * @class
 * @name DuColorSelector
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A color selector, with an edittext and a random button.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.colorSelector} to create a Selector.<br />
 * The Selector inherits the Group object from ScriptUI and has all of its properties and methods.
 * @category DuScriptUI
 */

/**
 * The current color
 * @type {DuColor}
 * @memberof DuColorSelector
 * @name color
 */

/**
 * Sets the current color
 * @method
 * @name setColor
 * @param {DuColor} color The new color
 * @memberof DuColorSelector
 */

/**
 * Method called when the has been changed
 * @type {DuColorSelector~onChange}
 * @name onChange
 * @memberof DuColorSelector
 */

/**
 * The function to execute when the color is changed.
 * @callback DuColorSelector~onChange
 * @memberof DuColorSelector
 */

/**
 * Creates a new color selector and adds it to the container
 * @param {Window|Panel|Group} container
 * @param {string} [helpTip] - The help tip to show on the selector
 * @returns {DuColorSelector}
 */
DuScriptUI.colorSelector = function ( container, helpTip )
{
    helpTip = def(helpTip, '');

    var colorSelector = container.add('group');
    colorSelector.orientation = 'row';
    colorSelector.margins = 0;
    colorSelector.spacing = 0;
    colorSelector.alignment = ['fill', 'top'];

    colorSelector.color = DuColor.Color.RX_PURPLE;

    var label = DuScriptUI.staticText( colorSelector, "Color:", DuColor.Color.RX_PURPLE );
    label.helpTip = helpTip;
    label.alignment = ['left','center'];

    var editText = DuScriptUI.editText( colorSelector, "A526C4", '#', '', '000000', helpTip );
    editText.alignment = ['fill', 'fill'];

    var randomButton = DuScriptUI.button( colorSelector, '', DuScriptUI.Icon.RANDOM, i18n._("Set a random value.") );
    randomButton.alignment = ['right', 'center'];

    colorSelector.onChange = function() {};

    function labelClicked()
    {
        var color = new DuColor( colorPicker(editText.text) );
        editText.setText( color.hex() );
        colorChanged();
    }

    function randomClicked()
    {
        var color = DuColor.random();
        editText.setText( color.hex() );
        colorChanged();
    }

    function colorChanged()
    {
        var color = DuColor.fromHex( editText.text );
        colorSelector.color = color;
        DuScriptUI.setTextColor( label, color );
        colorSelector.onChange();
    }

    colorSelector.setColor = function( color )
    {
        editText.setText( color.hex() );
        colorChanged();
    }

    label.addEventListener("mousedown",labelClicked,false);
    randomButton.onClick = randomClicked;
    editText.onChange = colorChanged;

    return colorSelector;
}// ==================== |-------------| ====================
// ==================== | multiButton | ====================
// ==================== |-------------| ====================


/**
 * @class
 * @name DuMultiButton
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A multi button popup.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.multiButton} to create a DuMultiButton.<br />
 * The DuMultiButton inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {StaticText} label - The current text
 * @property {DuButton[]} buttons  - The buttons
 * @property {string} text  - The current text
 * @property {DuMultiButton~build} build - You can use this callback to add a function which builds the UI of the popup, it will be called on first display.
 * @category DuScriptUI
 */

/**
 * You can use this callback to add a function which builds the content of the popup, it will be called on first display.<br />
 * This allows a faster startup of your script by delaying the creation of the tabs which are hidden at startup.<br />
 * You can use <code>this.addButton</code> to add buttons in the popup.
 * @callback DuMultiButton~build
 * @memberof DuMultiButton
 */
/**
 * Creates a multi button popup
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the multi button.
 * @param {string} [text] - The text.
 * @param {string|DuBInary} [image] - The path to the icon (or a PNG as a string representation). Default: empty string
 * @param {string} [helpTip] - The help tip to show on the multi button
 * @param {Boolean} [ignoreUIMode=false] - Will show texte even if the ui mode is set to > 1 in the script settings
 * @return {DuSelector} - The multiButton
 */
DuScriptUI.multiButton = function( container, text, image, helpTip, ignoreUIMode )
{
    var options = {};
    text = def(text, '');

    if (jstype(text) != 'string') options = text;
    options.text = def(options.text, text);

    if (options.text != '') options.text = options.text + '...';
    helpTip = def(helpTip, '');
    image = def(image, '');
    ignoreUIMode = def(ignoreUIMode, false);

    options.image = def(options.image, image);
    options.helpTip = def(options.helpTip, helpTip);
    options.ignoreUIMode = def(options.ignoreUIMode, ignoreUIMode);

    var uiMode = DuESF.scriptSettings.get("common/uiMode", 0);
    if (uiMode >= 2 && image != '' && !options.ignoreUIMode) options.text = '';

    var multiButton = container.add( 'group' );
    multiButton.orientation = 'row';
    multiButton.spacing = 0;
    multiButton.alignment = [ 'fill', 'top' ];
    multiButton.alignChildren = [ 'center', 'center' ];

    multiButton.mainGroup = multiButton.add( 'group' );
    multiButton.mainGroup.orientation = 'row';
    if (text != '') {
        multiButton.mainGroup.margins = 1;
        multiButton.mainGroup.spacing = 4;
    }
    multiButton.mainGroup.alignment = [ 'fill', 'fill' ];
    multiButton.mainGroup.alignChildren = [ 'center', 'center' ];
    multiButton.helpTip = helpTip;

    //the menu button
    if ( options.text != '' || (options.text == "" && options.image == ""))
    {
        multiButton.menuButton = multiButton.mainGroup.add( 'image', undefined, DuScriptUI.Icon.MORE.binAsString );
        multiButton.menuButton.alignment = [ 'left', 'center' ];
        multiButton.menuButton.helpTip = options.helpTip;
    }

    //the image
    if (options.image != '')
    {
        var iconGroup = multiButton.mainGroup.add('group');
        if ( options.text == '' ) {
            iconGroup.minimumSize = [16,16];
            iconGroup.alignment = ['center', 'fill'];
        }
        else {
            iconGroup.minimumSize = [20,20];
            iconGroup.margins = 2;
            iconGroup.alignment = ['left', 'fill'];
        }
        if (options.image instanceof DuBinary) options.image = options.image.binAsString;
        multiButton.icon = iconGroup.add( 'image', undefined, options.image );
        multiButton.icon.alignment = [ 'center', 'center' ];
        multiButton.icon.helpTip = options.helpTip;
    }

    if ( options.text != '')
    {
        //add fillers to be able to click anywhere on the button
        multiButton.fillerM = multiButton.mainGroup.add( 'statictext', undefined, " " );
        multiButton.fillerM.alignment = [ 'left', 'fill' ];
        multiButton.fillerM.size = [ 0, -1];

        //the text
        multiButton.label = multiButton.mainGroup.add( 'statictext', undefined, options.text );
        multiButton.label.helpTip = '';
        multiButton.label.alignment = [ 'fill', 'center' ];
        multiButton.label.helpTip = options.helpTip;

        //add fillers to be able to click anywhere on the button
        multiButton.fillerR = multiButton.mainGroup.add( 'statictext', undefined, " " );
        multiButton.fillerR.alignment = [ 'fill', 'fill' ];
    }

    //create popup
    multiButton.popup = new Window( 'palette', '', undefined,
    {
        borderless: true
    } );
    multiButton.popup.margins = 2;
    multiButton.popup.spacing = 0;
    multiButton.popup.closeButton = DuScriptUI.button( multiButton.popup, i18n._("Cancel") );
    multiButton.popup.closeButton.onClick = multiButton.popup.onDeactivate = function()
    {
        multiButton.popup.hide();
        DuScriptUI.eventFunctionsPaused = false;
    };
    multiButton.popup.buttons = multiButton.popup.add( 'group' );
    multiButton.popup.buttons.orientation = 'column';
    multiButton.popup.buttons.margins = 0;
    multiButton.popup.buttons.spacing = 0;
    multiButton.popup.hide();

    multiButton.build = function() {};
    multiButton.built = false;
    
    //add default button
    multiButton.buttons = [];

    multiButton.addButton = function( text, image, helpTip, addOptionsPanel, optionsWithoutButton, optionsButtonText )
    {
        multiButton.popup.hide();
        text = def(text, '-');
        image = def(image, '');
        helpTip = def(helpTip, '');

        if (multiButton.label)
        {
            var size = text.length * 7;
            if ( multiButton.label.minimumSize.width < size ) multiButton.label.minimumSize.width = size;
        }
        
        //add to popup
        var button = DuScriptUI.button( multiButton.popup.buttons,
            {
                text: text,
                image: image,
                helpTip: helpTip,
                options: addOptionsPanel,
                ignoreUIMode: true,
                optionsWithoutButton: optionsWithoutButton,
                optionsButtonText: optionsButtonText
            } );
        multiButton.buttons.push( button );
        button.bgColor = DuColor.Color.OBSIDIAN;
        button.dim();
        
        multiButton.popup.layout.layout(true);
        multiButton.popup.update();

        return button;
    }

    multiButton.clicked = function( e )
    {
        if (!multiButton.built)
        {
            multiButton.build();
            multiButton.built = true;
        }

        var x = e.screenX - e.clientX;
        var y = e.screenY - e.clientY;
        multiButton.popup.location = [ x, y ];
        multiButton.popup.show();
        DuScriptUI.eventFunctionsPaused = true;
    }

    //mouse over
    multiButton.highlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();
        if (multiButton.label)
        {
            DuScriptUI.setTextColor( multiButton.label, DuColor.Color.APP_HIGHLIGHT_COLOR );
        }
        else
        {
            DuScriptUI.setBackgroundColor( multiButton, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
        }
        
        DuScriptUI.highlightedControls.push( multiButton );
    }

    multiButton.dim = function( e )
    {
        if (multiButton.label) DuScriptUI.setTextColor( multiButton.label, DuColor.Color.APP_TEXT_COLOR );
        DuScriptUI.setBackgroundColor( multiButton, DuColor.Color.TRANSPARENT );
    }

    multiButton.mainGroup.addEventListener( "mouseover", multiButton.highlight );
    multiButton.mainGroup.addEventListener( "click", multiButton.clicked, true );

    multiButton.dim();

    return multiButton;
}// ==================== |-------------| ====================
// ==================== | progressbar | ====================
// ==================== |-------------| ====================

/**
    * Constructs a progress bar popup
    * @class DuProgressBar
    * @classdesc A simple progress bar.<br />
    * This was inspired by {@link https://github.com/indiscripts/extendscript/blob/master/scriptui/ProgressBar.jsx the progress bar}
    * by {@link  https://www.indiscripts.com/ Marc Autret / IndiScripts}.
    * @param {string} title The title of the progress bar
 * @category DuScriptUI
 */
DuProgressBar = function( )
{
    this.window = new Window('palette', '', [0,0,360,86]);
    this.stage = this.window.add( 'statictext', { x:20, y:15, width:320, height:16 }, i18n._("Working"));
    
    var barGroup = this.window.add('group', { x:20, y:37, width:320, height:12 });
    barGroup.orientation = 'stacked';
    barGroup.margins = 0;
    this.bar = barGroup.add( 'progressbar', { x:0, y:0, width:320, height:12 }, 0, 0);
    this.image = barGroup.add( 'image', { x:0, y:0, width:320, height:12 }, DuScriptUI.Icon.PROGRESS.binAsString );
    
    this.label = this.window.add( 'statictext', { x:20, y:51, width:320, height:16 });

    this.message =  i18n._("Magic is happening") + ' %1';

    this.window.center();

    // A private method to update with the message and the value
    this.__ = function()
    {
        var ok = this.bar.maxvalue != 0;
        if (ok) this.label.text = DuString.replace(this.message, '%1', this.bar.value + ' / ' + this.bar.maxvalue);
        else this.label.text = i18n._("Magic is happening") + '!';
        this.bar.visible = ok;
        this.image.visible = !ok;
        this.window.update();
    };
}

/**
 * Changes the text of the label
 * @param {string} message The text
 */
DuProgressBar.prototype.msg = function( message )
{
    if(isdef( message )) this.message = message;
    this.__();
}

/**
 * Changes the text of the current stage
 * @param {string} message The text
 */
DuProgressBar.prototype.stg = function( stage )
{
    this.stage.text = stage;
    this.msg();
}

/**
 * Shows the progress bar and updates the value and text
 * @param {string} [message=''] The text
 * @param {int} [maxValue=100] The maximum value
 */
DuProgressBar.prototype.show = function( message )
{
    this.window.show();
    this.msg(message);  
}

/**
 * Resets the progress bar to 0
 * @param {int} [maxValue=0] The new maximum value
 */
DuProgressBar.prototype.reset = function( maxValue )
{
    maxValue = def(maxValue, 0);
    this.bar.value = 0;
    this.bar.maxvalue = maxValue;
    this.stg( i18n._("Working") );
    this.msg( i18n._("Magic is happening") + ' %1' );
}

/**
 * Updates and increments the progress bar
 * @param {int} [value] The new value. if omitted, the bar is just incremented by 1
 * @param {string} [message] A new label
 */
DuProgressBar.prototype.hit = function( value, message )
{
    value = def(value, 1);
    this.bar.value += value;
    this.msg(message);
}

/**
 * Sets the maximum value
 * @param {int} maxValue The new maximum value
 * @param {Boolean} [onlyIfZero=true] Set to false to change the max value even if it was not 0 before
 */
DuProgressBar.prototype.setMax = function( maxValue, onlyIfZero )
{
    onlyIfZero = def(onlyIfZero, true);
    if (onlyIfZero && this.bar.maxvalue != 0) return;
    this.bar.maxvalue = maxValue;
    this.msg();
}

/**
 * Increments the maximum value
 * @param {int} [maxValue=1] The value to add to the maximum
 */
DuProgressBar.prototype.addMax = function( maxValue )
{
    maxValue = def(maxValue, 1);
    this.setMax(this.bar.maxvalue + maxValue, false);
}

/**
 * Hides the progress bar
 */
DuProgressBar.prototype.hide = function( )
{
    this.window.hide();
}

/**
 * Closes the progress bar
 */
DuProgressBar.prototype.close = function( )
{
    this.window.close();
}

DuProgressBar.init = function()
{
    /**
    * Show this progress bar before long operations with {@link DuProgressBar.show} and DuESF will update it.
    * @type {DuProgressBar}
    */
    DuScriptUI.progressBar = new DuProgressBar();
}
DuESF.initMethods.push( DuProgressBar.init );
// ==================== |---------| ====================
// ==================== | toolbar | ====================
// ==================== |---------| ====================

/**
 * Creates a toolbar with a lighter background
 * @param {Panel|Window|Group} container The ScriptUI Object which will contain and display the toolbar.
 * @param {int} [numCols] The number of columns to use when adding the buttons.
 * @returns {Group} The toolbar, a ScriptUI Group
 */
DuScriptUI.toolBar = function( container, numCols )
{
    var uiMode = DuESF.scriptSettings.get("common/uiMode", 0);

    if (!isdef( numCols ))
    {
        if (uiMode == 0) numCols = 3;
        else numCols = 6;
    }
    
    var toolsGroup = DuScriptUI.group( container, 'row' );
    toolsGroup.spacing = 3;
    toolsGroup.margins = 3;
    DuScriptUI.setBackgroundColor(toolsGroup , DuColor.Color.DARK_GREY );

    toolsGroup.columns = [];

    toolsGroup.spacing = 3;
    if (uiMode == 0) toolsGroup.alignChildren = ['fill', 'top'];
    else toolsGroup.alignChildren = ['center', 'top'];
    for (var i = 0; i < numCols; i++)
    {
        toolsGroup.columns.push( DuScriptUI.group(toolsGroup, 'column') );
    }

    toolsGroup.currentCol = 0;

    toolsGroup.addButton = function (text, icon, helpTip, addOptions, optionsWithoutPanel)
    {
        var options = {};
        text = def(text, '');

        if (jstype(text) != 'string') options = text;
        options.text = def(options.text, text);

        helpTip = def(helpTip, "");
        addOptions = def(addOptions, false);
        optionsWithoutPanel = def(optionsWithoutPanel, false);

        options.helpTip = def(options.helpTip, helpTip);
        options.options = def(options.options, addOptions);
        options.optionsWithoutPanel = def(options.optionsWithoutPanel, optionsWithoutPanel);
        options.image = def(options.image, icon);

        options.localize = false;
        options.orientation = uiMode == 0 ? 'column' : undefined;
        options.helpTip = uiMode == 0 ? helpTip : options.text + "\n\n" + helpTip;
        options.text =  uiMode == 0 ?  options.text : '';
        
        var button = DuScriptUI.button ( toolsGroup.columns[toolsGroup.currentCol], options);

        button.alignment = uiMode == 0 ? ['fill', 'top'] : ['center', 'top'];

        toolsGroup.currentCol = (toolsGroup.currentCol + 1) % numCols;


        return button;
    }

    return toolsGroup;
}
// ==================== |---------| ====================
// ==================== | library | ====================
// ==================== |---------| ====================

/**
 * @class
 * @name DuLibrary
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A complete library interface.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.library} to create a Library.<br />
 * The Library inherits the <code>Group</code> object from ScriptUI and has all of its properties and methods.
 * @property {ListBox} list The Listbox with the current items for the library.
 * Items are extended with new properties, see {@link DuListItem} for more details.
 * @property {DuLibraryItem} library The associated library data. Use setLibrary to change it.
 * @property {DuLibrary~onRun} onRun The function used to run the selected item.
 * @property {DuLibrary~onAltRun} onAltRun The function used to run the selected item with alt click.
 * @property {DuLibrary~onCtrlRun} onCtrlRun The function used to run the selected item with ctrl click.
 * @property {DuLibrary~onCtrlAltRun} onCtrlAltRun The function used to run the selected item with ctrl alt click.
 * @property {DuLibrary~onEditData} onEditData The function to execute to edit data.
 * @property {DuLibrary~onFolderOpened} onFolderOpened The function to open a containing folder
 * @property {DuLibrary~onFolderEdited} onFolderEdited The function to edit the folder
 * @property {DuLibrary~onAddItem} onAddItem The function to execute when adding a new item.
 * @property {DuLibrary~onEditItem} onEditItem The function to execute when editing an item.
 * @property {DuLibrary~onRemoveItem} onRemoveItem The function to execute when removing an item.
 * @property {DuLibrary~onRefresh} onRefresh The function to execute to refresh the library.
 * @property {DuLibrary~setLibrary} setLibrary Sets a new library. May be called from onRefresh to replace the lib by a new one.
 * @property {DuLibrary~setLibrary} runItem Runs the selected item. Tied to the apply/run button by default.
 * @extends {Group}
 * @category DuScriptUI
 */

/**
 * The function to execute to refresh the library.
 * @callback DuLibrary~onRefresh
 * @param {DuLibraryItem} category The category to refresh.
 * @memberof DuLibrary
 */

/**
 * Sets a new library. May be called from onRefresh to replace the lib by a new one.
 * @callback DuLibrary~setLibrary
 * @param {DuLibraryItem} newLib The new library.
 * @memberof DuLibrary
 */

/**
 * The function used to run the selected item.
 * @callback DuLibrary~onRun
 * @param {DuListItem} item The item to run/apply.
 * @memberof DuLibrary
 */

/**
 * The function used to run the selected item.
 * @callback DuLibrary~onAltRun
 * @param {DuListItem} item The item to run/apply.
 * @memberof DuLibrary
 */

/**
 * The function used to run the selected item.
 * @callback DuLibrary~onCtrlRun
 * @param {DuListItem} item The item to run/apply.
 * @memberof DuLibrary
 */

/**
 * The function used to run the selected item.
 * @callback DuLibrary~onCtrlAltRun
 * @param {DuListItem} item The item to run/apply.
 * @memberof DuLibrary
 */

/**
 * The function used to edit an item data.
 * @callback DuLibrary~onEditData
 * @param {DuListItem} item The item to edit.
 * @memberof DuLibrary
 */

/**
 * The function to open a containing folder.
 * @callback DuLibrary~onFolderOpened
 * @param {DuListItem} item The item to edit.
 * @param {Object} category The current category.
 * @memberof DuLibrary
 */

/**
 * The function to edit the folder.
 * @callback DuLibrary~onFolderEdited
 * @param {DuListItem} item The item to edit.
 * @param {Object} category The current category.
 * @memberof DuLibrary
 */

/**
 * The function to execute when adding a new item.
 * @callback DuLibrary~onAddItem
 * @param {Object} category The current category.
 * @memberof DuLibrary
 */

/**
 * The function to execute when editing an item.
 * @callback DuLibrary~onAddItem
 * @param {DuListItem} item The item to edit.
 * @param {Object} category The current category.
 * @memberof DuLibrary
 */

/**
 * The function to execute when removing an item.
 * @callback DuLibrary~onRemoveItem
 * @param {DuListItem} item The item to remove.
 * @param {Object} category The current category.
 * @memberof DuLibrary
 */

/**
 * @class
 * @name DuListItem
 * @classdesc For use with {@link DuScriptUI}.<br />
 * An item in a custom listbox used with {@link DuLibrary}.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.library} to create a Library.<br />
 * The Library has a <code>list</code> property which is a ScriptUI <code>ListBox</code> which contains these DuListItem.<br />
 * DuListItem inherits the <code>Item</code> object from ScriptUI and has all of its properties and methods.
 * @property {*} data The data associated with the item, depends on the library object passed to the DuLibrary.
 * @property {string} libType The type of item, one of ['item', 'category', 'parent']
 * @property {bool} editableData Wether this item data can be edited by the user.
 * @property {bool} editableItem Whether this item can be edited by the user.
 * @category DuScriptUI
 */

/**
 * @class
 * @name DuLibraryItem
 * @classdesc For use with {@link DuLibrary}.<br />
 * An item in a library.
 * @hideconstructor
 * @property {*} data The data associated with the item.
 * @property {string} libType The type of item, one of ['item', 'category']
 * @property {bool} editableData Wether this item data can be edited by the user.
 * @property {bool} editableItem Whether this item can be edited by the user.
 * @property {string} icon Either the path to an image or a png representation as a string.
 * @category DuScriptUI
 */

/**
 * Adds a new {@link DuLibrary} to the container.
 * @param {Panel|Window|Group} container The ScriptUI Object which will contain and display the library.
 * @param {DuLibraryItem} library A library object. Key/value pairs, values being {@link DuLibraryItem} objects, keys being their display name.
 * @param {object} [options] Options and other values.
 */
DuScriptUI.library = function( container, library, options ) {
    options = def(options, {});
    options.runButton = def(options.runButton, true);
    options.editDataButton = def(options.editDataButton, false);
    options.folderButton = def(options.folderButton, true);
    options.canEditFolder = def(options.canEditFolder, false);
    options.editListButtons = def(options.editListButtons, true);
    options.refreshInterval = def(options.refreshInterval, -1);
    options.defaultItemIcon = def(options.defaultItemIcon, '');
    options.itemName = def(options.itemName, i18n._p("project", "Item")); /// TRANSLATORS: an item in the After Effects project
    options.runHelpTip = def(options.runHelpTip, i18n._p("file", "Run")); /// TRANSLATORS: as in "run script"
    options.folderHelpTip = def(options.folderHelpTip, i18n._("Open folder"));
    options.addItemHelpTip = def(options.addItemHelpTip, i18n._("Add item or category"));
    options.editItemHelpTip = def(options.editItemHelpTip, i18n._("Edit item or category"));
    options.removeItemHelpTip = def(options.removeItemHelpTip, i18n._("Remove item or category"));
    options.editDataHelpTip = def(options.editDataHelpTip, '');
    options.sortButton = def(options.sortButton, true);
    options.refreshButton = def(options.refreshButton, false);

    // The main group
    var duLibrary = container.add('group');
    duLibrary.orientation = 'column';
    duLibrary.alignment = ['fill', 'fill'];
    duLibrary.margins = 0;
    duLibrary.spacing = 0;
    // And its lib
    duLibrary.library = library;

    var runIcon = '';
    if (options.runButton) runIcon = DuScriptUI.Icon.RUN;
    else runIcon = DuScriptUI.Icon.CHECK;

    // UTILS
    duLibrary.currentCategory = duLibrary.library;
    var currentCommand = [];
    duLibrary.sortMode = 'up';

    function setCategory( cat ) {
        duLibrary.onRefresh( cat );

        duLibrary.currentCategory = cat;

        // Update the sort button
        duLibrary.srtButton.freeze = true;
        if (duLibrary.sortMode == 'none') duLibrary.srtButton.setCurrentIndex(0);
        else if (duLibrary.sortMode == 'down') duLibrary.srtButton.setCurrentIndex(2);
        else duLibrary.srtButton.setCurrentIndex(1);
        duLibrary.srtButton.freeze = false;

        if (duLibrary.sortMode != 'none') cat = DuJSObj.sort(cat, false, function( a, b ) {
            var aType = cat[a].libType;
            var bType = cat[b].libType;
            var r = 1;
            if (duLibrary.sortMode == 'down') r = -1;
            // Categories go first no matter what
            if (aType == 'category' && bType == 'item') return -r;
            else if (aType == 'item' && bType == 'category') return r;
            // Alphabetical
            if (a < b) return -r;
            if (a > b) return r;            
            return 0;
        });

        libList.removeAll();

        if (duLibrary.currentCategory != duLibrary.library) {
            var item = libList.add('item', '..');
            item.image = DuScriptUI.Icon.PARENT.binAsString;
            item.libType = 'parent';
            item.data = '';
            item.editableItem = false;
            item.editableData = false;
        }

        for (i in cat) {
            if (!cat.hasOwnProperty(i)) continue;
            // ignore lib properties
            if (i == 'libType') continue;
            if (i == 'icon') continue;
            if (i == 'data') continue;
            if (i == 'editableData') continue;
            if (i == 'editableItem') continue;
            var val = cat[i];
            // ignore unknown types
            if (typeof val.libType === 'undefined') continue;
            if (val.libType == 'item') {
                val.icon = def(val.icon, options.defaultItemIcon);
                if (val.icon == '') val.icon = options.defaultItemIcon;
                val.data = def (val.data, '');
                val.editableData = def (val.editableData, false);
                val.editableItem = def (val.editableItem, false);

                var item = libList.add('item', i);
                item.libType = 'item';
                item.data = val.data;
                item.editableItem = val.editableItem;
                item.editableData = val.editableData;
                try {  item.image = val.icon; }
                catch(e) {}
            }
            else {
                val.icon = def(val.icon, DuScriptUI.Icon.FOLDER_CLOSED.binAsString);
                val.data = def (val.data, '');
                val.editableData = false;
                val.editableItem = def (val.editableItem, false);

                var item = libList.add('item', i);
                item.libType = 'category';
                item.data = val.data;
                item.editableItem = val.editableItem;
                item.editableData = val.editableData;
                try {  item.image = val.icon; }
                catch(e) {}
            }
        }

        libList.onChange();
    }

    function updateCategory() {
        var cat = duLibrary.library;
        duLibrary.onRefresh( cat );
        for (var i = 0; i < currentCommand.length; i++) {
            if (!cat[currentCommand[i]]) break;
            cat = cat[currentCommand[i]];
            if (cat) duLibrary.onRefresh( cat );
        }
        setCategory(cat);
    }

    function updateBreadCrumbs() {
        tipLabel.text = currentCommand.join(' > ');
    }

    function parseCmd() {
        var currentCmd = cmdEdit.tempText;

        if ( DuString.endsWith(currentCmd, ' >')) return;
        if ( DuString.fullTrim(currentCmd) == '') {
            updateCategory();
            return;
        }
        var newBlock = DuString.endsWith(currentCmd, ' ');

        if (!newBlock) {
            search = currentCmd.toLowerCase();
            // Reset list
            setCategory(duLibrary.currentCategory);
            for (var i = libList.items.length -1; i >= 0; i--) {
                var item = libList.items[i];
                if ( // unsuccessful search
                    item.text.toLowerCase().indexOf(search) < 0
                    )
                {
                    libList.remove(i);
                }
            }
            return;
        }
        if (libList.items.length > 0 && currentCmd.length > 0) {
            var item = libList.items[0];
            if (item.libType == 'parent') {
                currentCommand.pop();
                updateBreadCrumbs();
                updateCategory();
                cmdEdit.setText('');
            }
            else if (item.libType == 'category') {
                currentCommand.push(item.text);
                updateBreadCrumbs();
                updateCategory();
                cmdEdit.setText('');
            }
            /*else if (item.libType == 'item') {
                cmdEdit.setText(item.text + ' >');
                updateCategory();
            }*/
        }
    };

    function run() {
        parseCmd();
        cmdEdit.setText(cmdEdit.text + ' ');
        parseCmd();
        if (libList.items.length < 0) return;
        var item = libList.items[0];
        if (item.libType == 'item') {
            duLibrary.onRun(item);
            // Clears the edit
            cmdEdit.setText('');
            parseCmd();
        }
    }

    duLibrary.runItem = function ( modifier ) {
        var item = libList.selection;
        if (!item) return;
        modifier = def(modifier, '');

        if (item.libType == 'parent') {
            currentCommand.pop();
            updateBreadCrumbs();
            updateCategory();
            cmdEdit.setText('');
            return;
        }

        if (item.libType == 'category') {
            currentCommand.push(item.text);
            updateBreadCrumbs();
            updateCategory();
            cmdEdit.setText('');
            return;
        }
        if (modifier == 'alt') duLibrary.onAltRun(item);
        else if (modifier == 'ctrl') duLibrary.onCtrlRun(item);
        else if (modifier == 'ctrlAlt') duLibrary.onCtrlAltRun(item);
        else duLibrary.onRun(item);
        // Clears the edit
        cmdEdit.setText('');
        parseCmd();
    };

    duLibrary.clear = function () {
        var currentCmd = cmdEdit.text;
        // Empty
        if ( DuString.fullTrim(currentCmd) == '') {
            currentCommand = [];
            updateBreadCrumbs();
            updateCategory();
        }
        cmdEdit.setText('');
        parseCmd();
    };

    duLibrary.refresh = function () {
        duLibrary.onRefresh(duLibrary.currentCategory);
        setCategory(duLibrary.currentCategory);
        updateBreadCrumbs();
    };

    duLibrary.sort = function(mode) {
        duLibrary.sortMode = mode;
        duLibrary.onRefresh(duLibrary.currentCategory);
        setCategory(duLibrary.currentCategory);
        updateBreadCrumbs();
    };

    // CREATE UI

    var cmdGroup = DuScriptUI.group(duLibrary, 'row');
    cmdGroup.spacing = 3;
    cmdGroup.alignment = ['fill', 'top'];

    var clearButton = DuScriptUI.button(
        cmdGroup,
        '',
        DuScriptUI.Icon.CLOSE,
        i18n._("Remove all")
    );
    clearButton.alignment = ['left', 'fill'];
    clearButton.onClick = duLibrary.clear;

    var cmdEdit = DuScriptUI.editText(
        cmdGroup,
        '',
        '',
        '',
        i18n._("Start typing...") /// TRANSLATORS: start typing a command in a command line interface
    );
    cmdEdit.alignment = ['fill', 'fill'];
    cmdEdit.onChanging = parseCmd;
    cmdEdit.onChange = parseCmd;
    cmdEdit.onEnterPressed = run;

    var tipGroup = DuScriptUI.group( duLibrary, 'row');
    tipGroup.alignment = ['fill', 'top'];

    var tipLabel = DuScriptUI.staticText(
        tipGroup,
        ''
    );
    tipLabel.alignment = ['fill', 'fill'];

    var listGroup = DuScriptUI.group( duLibrary, 'row');
    listGroup.alignment = ['fill', 'fill'];

    var libList = listGroup.add('listbox');
    libList.alignment = ['fill', 'fill'];
    libList.onDoubleClick = duLibrary.runItem;
    libList.onChange = function() {
        var item = libList.selection;
        if (options.editDataButton) {
            if (!item) duLibrary.editDataButton.visible = false;
            else duLibrary.editDataButton.visible = item.editableData;
        }

        if (options.editListButtons) {
            if (!item) {
                duLibrary.editItemButton.visible = false;
                removeItemButton.visible = false;
            }
            else {
                duLibrary.editItemButton.visible = item.editableItem; 
                removeItemButton.visible = duLibrary.currentCategory.editableItem && item.editableItem;
            }
            addItemButton.visible = duLibrary.currentCategory.editableItem;
            
        }
    };

    var listButtonsGroup = DuScriptUI.group( listGroup, 'column');
    listButtonsGroup.alignment = ['right', 'fill'];

    duLibrary.runButton = DuScriptUI.button(
        listButtonsGroup,
        '',
        runIcon,
        options.runHelpTip
    );
    duLibrary.runButton.alignment = ['right','top'];
    duLibrary.runButton.onClick = duLibrary.runItem;
    duLibrary.runButton.onAltClick = function() { duLibrary.runItem('alt') };
    duLibrary.runButton.onCtrlClick = function() { duLibrary.runItem('ctrl') };
    duLibrary.runButton.onCtrlAltClick = function() { duLibrary.runItem('ctrlAlt') };

    if (options.editDataButton) {
        duLibrary.editDataButton = DuScriptUI.button(
            listButtonsGroup,
            '',
            DuScriptUI.Icon.EDIT,
            options.editDataHelpTip
        );
        duLibrary.editDataButton.alignment = ['right','top'];
        duLibrary.editDataButton.onClick = function() { 
            var selection = libList.selection;
            if (!selection) return;
            if (!selection.editableData) return;
            duLibrary.onEditData( selection );
        };
        duLibrary.editDataButton.onAltClick = function() { 
            var selection = libList.selection;
            if (!selection) return;
            if (!selection.editableData) return;
            duLibrary.onAltEditData( selection );
        };
    }

    if (options.folderButton) {
        var fldrButton = DuScriptUI.button(
            listButtonsGroup,
            '',
            DuScriptUI.Icon.FOLDER_CLOSED,
            options.folderHelpTip
        );
        fldrButton.alignment = ['right','top'];
        fldrButton.onClick = function() {
            var selection = libList.selection;
            duLibrary.onFolderOpened(selection, duLibrary.currentCategory);
        };
        if (options.canEditFolder) {
            fldrButton.onAltClick = function() {
                var selection = libList.selection;
                duLibrary.onFolderEdited(selection, duLibrary.currentCategory);
            };
        }
    }

    if (options.sortButton) {
        duLibrary.srtButton = DuScriptUI.selector( listButtonsGroup, '', true );
        duLibrary.srtButton.alignment = ['right','top'];
        duLibrary.srtButton.addButton(
            '',
            DuScriptUI.Icon.SORT
        );
        duLibrary.srtButton.addButton(
            '',
            DuScriptUI.Icon.SORT_UP
        );
        duLibrary.srtButton.addButton(
            '',
            DuScriptUI.Icon.SORT_DOWN
        );
        duLibrary.srtButton.setCurrentIndex(1);
        duLibrary.srtButton.onChange = function() {
            if (duLibrary.srtButton.index == 0) duLibrary.sort('none');
            else if (duLibrary.srtButton.index == 1) duLibrary.sort('up');
            else if (duLibrary.srtButton.index == 2) duLibrary.sort('down');
        }
    }

    if (options.refreshButton) {
        var rfrshButton = DuScriptUI.button(
            listButtonsGroup,
            '',
            DuScriptUI.Icon.UPDATE,
            i18n._("Refresh") /// TRANSLATORS: refresh a list
        );
        rfrshButton.alignment = ['right','top'];
        rfrshButton.onClick = duLibrary.refresh;
    }

    if (options.editListButtons) {
        var addItemButton = DuScriptUI.multiButton(
            listButtonsGroup,
            '',
            DuScriptUI.Icon.ADD,
            options.addItemHelpTip
        );
        addItemButton.alignment = ['right','bottom'];
        duLibrary.addItemButton = addItemButton.addButton(
            options.itemName
        );
        duLibrary.addCategoryButton = addItemButton.addButton(
            i18n._("Category")
        );

        duLibrary.addItemButton.onClick = function() {
            duLibrary.onAddItem(duLibrary.currentCategory);
        };
        duLibrary.addCategoryButton.onClick = function() {
            duLibrary.onAddCategory(duLibrary.currentCategory);
        }

        duLibrary.editItemButton = DuScriptUI.button(
            listButtonsGroup,
            '',
            DuScriptUI.Icon.EDIT,
            options.editItemHelpTip
        );
        duLibrary.editItemButton.alignment = ['right','bottom'];
        duLibrary.editItemButton.onClick = function() {
            var selection = libList.selection;
            if (!selection) return;
            if (!selection.editableItem) return;
            duLibrary.onEditItem(selection, duLibrary.currentCategory);
        };

        var removeItemButton = DuScriptUI.button(
            listButtonsGroup,
            '',
            DuScriptUI.Icon.REMOVE,
            options.removeItemHelpTip
        );
        removeItemButton.alignment = ['right','bottom'];
        removeItemButton.onClick = function() {
            var selection = libList.selection;
            if (!selection) return;
            if (selection.libType == 'parent') return;
            // Add confirm
            duLibrary.onRemoveItem(selection, duLibrary.currentCategory);
        };
    }

    duLibrary.onRun = function() {};
    duLibrary.onAltRun = function() {};
    duLibrary.onCtrlRun = function() {};
    duLibrary.onCtrlAltRun = function() {};
    duLibrary.onEditData = function() {};
    duLibrary.onAltEditData = function() {};
    duLibrary.onFolderOpened = function() {};
    duLibrary.onFolderEdited = function() {};
    duLibrary.onAddItem = function() {};
    duLibrary.onAddCategory = function() {};
    duLibrary.onEditItem = function() {};
    duLibrary.onRemoveItem = function() {};
    duLibrary.onRefresh = function() {};

    duLibrary.setLibrary = function (newLib) {
        duLibrary.library = newLib;
        updateCategory();
    }

    duLibrary.list = libList;

    // Init 
    setCategory(duLibrary.library);

    // Refresh
    if (options.refreshInterval > 0) DuScriptUI.addEvent(duLibrary.refresh, 10000);

    return duLibrary;
}// ==================== |--------------| ====================
// ==================== | settingField | ====================
// ==================== |--------------| ====================

/**
 * @class
 * @name DuSettingField
 * @classdesc For use with {@link DuScriptUI}.<br />
 * An Setting field, which can be enabled or disabled.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.settingField} to create setting field.<br />
 * The DuSettingField inherits the <code>Group</code> object from ScriptUI and has all of its properties and methods.
 * @property {DuSettingField~onClick} onClick - The function to execute when the checkbox is clicked
 * @category DuScriptUI
 */

/**
 * The function to execute when the checkbox is clicked.
 * @callback DuSettingField~onClick
 * @memberof DuSettingField
 */

/**
 * Creates a {@link DuSettingField} which can be enabled or disabled
 * @param {Panel|Window|Group}		container	- The ScriptUI Object which will contain and display the setting.
 * @param {string}					[text]		- The label of the setting
 * @param {int}					[minimumLabelWidth]		- The minmimum width of the label
 * @return {DuSettingField}			The setting created.
 */
DuScriptUI.settingField = function(container, text, minimumLabelWidth ) {
    minimumLabelWidth = def(minimumLabelWidth, -1);

    var settingGroup = DuScriptUI.group(container, 'row');
    settingGroup.margin = 0;
    settingGroup.spacing = 3;
    settingGroup.alignment = ['fill', 'top'];

    var checkbox = DuScriptUI.checkBox( settingGroup, {
        text: text + ':',
        localize: false
    });
    checkbox.alignment = ['left', 'center'];
    checkbox.minimumSize = [minimumLabelWidth, -1];

    settingGroup = DuScriptUI.group(settingGroup, 'row');
    settingGroup.spacing = 0;
    settingGroup.margin = 0;
    settingGroup.alignment = ['fill', 'fill'];
    settingGroup.checked = settingGroup.enabled = false;

    settingGroup.onClick = function() {};

    settingGroup.setChecked = function (c) {
        checkbox.setChecked(c);
        settingGroup.enabled = c;
    };
    
    checkbox.onClick = function() {
        settingGroup.checked = checkbox.value;
        settingGroup.enabled = checkbox.value;
        settingGroup.onClick();
    };

    return settingGroup;
};
//low-level list of functions to be run when the user interacts with the UI
DuScriptUI.eventFunctions = [];

/**
 * Adds a function to be run periodically, which will be connected to several UI events, fired when the user interacts with the UI.
 * @param {function} func The function to connect to the UI
 * @param {int} [timeOut=3000] A timeOut in milliseconds which prevents the function to be run too often
 * @return {int} a unique identifier to be used to remove the function later, with {@link DuScriptUI.removeEvent}.
 */
DuScriptUI.addEvent = function (func, timeOut)
{
    timeOut = def(timeOut, 3000);
    func.id = new Date().getTime();
    func.timeOut = timeOut;
    func.lastFire = func.id;
    DuScriptUI.eventFunctions.push(func);
    return func.id;
}

/**
 * Removes a function previously added with {@link DuScriptUI.addEvent}.
 * @param {int} id The id of the function
 */
DuScriptUI.removeEvent = function (id)
{
    for (var i = 0, n = DuScriptUI.eventFunctions.length; i < n; i++)
    {
        var f = DuScriptUI.eventFunctions[i];
        if (f.id == id)
        {
            DuScriptUI.eventFunctions.splice(i,1);
            break;
        }
    }
}

/**
 * The default alignment of children of containers with "column" orientation
 * @type {String[]}
 * @default ["fill","top"]
 */
DuScriptUI.defaultColumnAlignment = [ "fill", "top" ];

/**
 * The default alignment of children of containers with "row" orientation
 * @type {String[]}
 * @default ["left","center"]
 */
DuScriptUI.defaultRowAlignment = [ "left", "center" ];

/**
 * The default alignment of children of containers with "stack" orientation
 * @type {String[]}
 * @default ["fill","top"]
 */
DuScriptUI.defaultStackAlignment = [ "fill", "fill" ];

/**
 * The default spacing of containers
 * @type {int}
 * @default 2
 */
DuScriptUI.defaultSpacing = 2;

/**
 * The default margins of containers
 * @type {int}
 * @default 2
 */
DuScriptUI.defaultMargins = 2;

/**
 * A bar used to show current funding status.
 * @type {ProgressBar}
 */
DuScriptUI.fundingBar = null;

/**
 * A label used to show current funding status.
 * @type {StaticText}
 */
DuScriptUI.fundingLabel = null;

//low-level undocumented list: keeps the currently highlighted controls
DuScriptUI.highlightedControls = [];

//low-level undocumented list: keeps all the tabs, used in the buildAllTabs method
DuScriptUI.allTabs = [];

//low-level undocumented flag: set to true when showUI has been called. Before this, there's no need to set layout for children, this will improve performance greatly.
DuScriptUI.uiShown = false;

//low-level undocumented flag: pause events firing (used with popups)
DuScriptUI.eventFunctionsPaused = false;

//low-level undocumented function: dims all currently highlighted controls
//Also runs all functions in DuScriptUI.eventFunctions
DuScriptUI.dimControls = function( )
{
    if (!DuScriptUI.eventFunctionsPaused)
    {
        var now = new Date().getTime();
        for (var i = 0, n = DuScriptUI.eventFunctions.length; i < n; i++)
        {
            var f = DuScriptUI.eventFunctions[i];
            if (now - f.lastFire < f.timeOut) continue;
            f();
            f.lastFire = now;
        }
    }

    if ( DuScriptUI.highlightedControls.length == 0 ) return;
    for ( var i = DuScriptUI.highlightedControls.length - 1; i >= 0; i-- )
    {
        try
        {
            DuScriptUI.highlightedControls[ i ].dim();
        }
        catch ( e )
        {};
        DuScriptUI.highlightedControls.pop();
    }
}

// ==================== |-----| ====================
// ==================== | oco | ====================
// ==================== |-----| ====================

/**
 * Methods to load and create OCO Documents and armatures
 * @namespace
 * @category DuOCO
 */
var DuOCO = {};

/**
 * Types of OCO armatures
 * @enum {string}
 */
DuOCO.Type = {
    /**
     * A meta-rig, used to automatically create a default armature to be used with an auto-rig.
     */
    META: 'meta',
    /**
     * An actual (rigged) character.
     */
    CHARACTER: 'character'
}

/**
 * Predefined limbs
 * @enum {string}
 */
DuOCO.Limb = {
    SPINE: 'spine',
    ARM: 'arm',
    LEG: 'leg',
    TAIL: 'tail',
    WING: 'wing',
    HAIR: 'hair',
    SNAKE_SPINE: 'snakeSpine',
    FISH_SPINE: 'fishSpine',
    FIN: 'fin',
    CUSTOM: 'custom'
}

/**
 * Some bone identifierss
 * @enum {string}
 */
DuOCO.Bone = {
    CUSTOM: 'custom',
    TIP: 'tip',
    CLAVICLE: 'clavicle',
    HUMERUS: 'humerus',
    RADIUS: 'radius',
    CARPUS: 'carpus',
    FINGER: 'finger',
    HEEL: 'heel',
    FEMUR: 'femur',
    TIBIA: 'tibia',
    TARSUS: 'tarsus',
    TOE: 'toe',
    HIPS: 'hips',
    SPINE: 'spine',
    TORSO: 'torso',
    NECK: 'neck',
    SKULL_TIP: 'skullTip',
    SKULL: 'skull',
    TAIL_ROOT: 'tail1',
    TAIL_MID: 'tail2',
    TAIL_END: 'tail3',
    FEATHER: 'feather',
    SNAKE_SPINE_ROOT: 'snakeSpine1',
    SNAKE_SPINE_MID: 'snakeSpine2',
    SNAKE_SPINE_END: 'snakeSpine3',
    FISH_SPINE_ROOT: 'fishSpine1',
    FISH_SPINE_MID: 'fishSpine2',
    FISH_SPINE_END: 'fishSpine3',
    HAIR_ROOT: 'snakeSpine1',
    HAIR_MID: 'snakeSpine2',
    HAIR_END: 'snakeSpine3',
    FIN: 'fin',
    FIN_FISHBONE: 'finBone'
}

/**
 * Types of limbs
 * @enum {string}
 */
DuOCO.LimbType = {
    HOMINOID: 'hominoid',
    PLANTIGRADE: 'plantigrade',
    DIGITIGRADE: 'digitigrade',
    UNGULATE: 'ungulate',
    ARTHROPOD: 'arthropod',
    CUSTOM: 'custom'
}

/**
 * Sides for the limbs Use these with {@link DuOCO.Location} to differenciate similar limbs,<br />
 * for example, a leg can be Front-Right, Front-Left, Back-Right, Back-Left, etc.
 * @enum {string}
 */
DuOCO.Side = {
    LEFT: "L",
    RIGHT: "R",
    NONE: ''
}

/**
 * Locations for the limbs. Use these with {@link DuOCO.Side} to differenciate similar limbs,<br />
 * for example, a leg can be Front-Right, Front-Left, Back-Right, Back-Left, etc.
 * @enum {string}
 */
DuOCO.Location = {
    FRONT: "Fr",
    BACK: "Bk",
    TAIL: "Tl",
    MIDDLE: "Md",
    ABOVE: "Ab",
    UNDER: "Un",
    NONE: ''
}

DuOCO.View = {
    FRONT: 0,
    LEFT: 1,
    RIGHT: 2,
    BACK: 3,
    TOP: 4,
    BOTTOM: 5
}

/**
 * Gets the boundaries of an armature (a chain of bones)
 * @param {DuOCOBone[]} armature The chain of bones
 * @returns {float[]} [left, top, right, bottom]
 */
DuOCO.getBounds = function ( armature )
{
    var left = 9999;
    var right = -9999;
    var top = 9999;
    var bottom = -9999;

    for (var i = 0, n = armature.length; i < n; i++)
    {
        var b = armature[i];
        // Check current bone
        if (b.x < left) left = b.x;
        if (b.x > right) right = b.x;
        if (b.y < bottom) bottom = b.y;
        if (b.y > top) top = b.y;
        // Check children
        var childBounds = DuOCO.getBounds( b.children );
        if (childBounds[0] < left) left = childBounds[0];
        if (childBounds[1] < top) top = childBounds[1];
        if (childBounds[2] < right) right = childBounds[2];
        if (childBounds[3] < bottom) bottom = childBounds[3];
        // Check other limbs
        for ( var j = 0, nL = b.limbs.length; j < nL; j++ )
        {
            var limbBounds = b.limbs[i].bounds();
            if (limbBounds[0] < left) left = limbBounds[0];
            if (limbBounds[1] < top) top = limbBounds[1];
            if (limbBounds[2] < right) right = limbBounds[2];
            if (limbBounds[3] < bottom) bottom = limbBounds[3];
        }
    }

    return [left, top, right, bottom];
}

/**
 * Creates a new OCO Document.
 * @class
 * @classdesc An Open Cut-Out character or meta-rig document.
 * @param {string} name The name of the character or the meta rig
 * @category DuOCO
 */
function DuOCODoc(name)
{
    /**
     * The type of this OCO Doc
     * @name type
     * @memberof DuOCODoc
     * @type {string}
     */
    this.type = DuOCO.Type.META;
    /**
     * The name of this OCO Doc
     * @name name
     * @memberof DuOCODoc
     * @type {string}
     */
    this.name = name;
    /**
     * The height of the character. Should always be 1!
     * @name height
     * @memberof DuOCODoc
     * @type {float}
     */
    this.height = 185.0;
    /**
     * The width of the character.
     * @name width
     * @memberof DuOCODoc
     * @type {float}
     */
    this.width = 60.0,
    /**
     * The coordinates of the center of mass of the character. [X, Y].
     * @name centerOfMass
     * @memberof DuOCODoc
     * @type {float[]}
     */
    this.centerOfMass = [0.0, 107.0];
    /**
     * The limbs/ Armatures
     * @name limbs
     * @memberof DuOCOLimb
     * @type {DuOCOLimb[]}
     */
    this.limbs = [];
    this.resolution = [1920,1080];
    this.world = [960,980];
    this.pixelsPerCm = 4.22;
}

/**
 * Gets the boundaries of the doc
 * @returns {float[]} [left, top, right, bottom]
 */
DuOCODoc.prototype.bounds = function ()
{
    var left = 9999;
    var right = -9999;
    var top = 9999;
    var bottom = -9999;

    for (var i = 0, n = this.limbs.length; i < n; i++)
    {
        var limbBounds = this.limbs[i].bounds();
        if (limbBounds[0] < left) left = limbBounds[0];
        if (limbBounds[1] < top) top = limbBounds[1];
        if (limbBounds[2] < right) right = limbBounds[2];
        if (limbBounds[3] < bottom) bottom = limbBounds[3];
    }

    return [left, top, right, bottom];
}

/**
 * Updates the width and height of the character, according to the content.<br />
 * This method should be called each time a limb/bone is added/removed/edited and the bounds may change.
 */
DuOCODoc.prototype.updateSize = function()
{
    // Update doc width & height
    var bounds = this.bounds();
    // right - left
    this.width = this.bounds[2] - this.bounds[0];
    // top - bottom
    this.height = this.bounds[1] - this.bounds[3];
}

/**
 * Creates a new limb and adds it to the doc
 * @param {DuOCO.Limb} [limb=DuOCO.Limb.CUSTOM] A Predefined limb
 * @param {DuOCO.Side} [side=DuOCO.Side.NONE] - The side of the limb
 * @param {DuOCO.Location} [location=DuOCO.Location.NONE] - The location of the limb
 * @param {DuOCO.LimbType} [type=DuOCO.LimbType.CUSTOM] - The type of the limb
 * @return {DuOCOLimb} The new limb
 */
DuOCODoc.prototype.newLimb = function( limb, side, location, type )
{
    var l = new DuOCOLimb( limb, side, location, type );
    this.limbs.push(l);
    return l;
}

/**
 * Creates a new arm.
 * @param {DuOCO.LimbType} [type=DuOCO.LimbType.HOMINOID] The type of limb
 * @param {DuOCO.Side} [side=DuOCO.Side.LEFT] The side
 * @param {Boolean} [shoulder=false] Whether to create a shoulder
 * @param {Boolean} [arm=true]  Whether to create an arm / humerus
 * @param {Boolean} [forearm=true]  Whether to create a forearm
 * @param {Boolean} [hand=true]  Whether to create a hand
 * @param {Boolean} [claws=false]  Whether to add claws
 * @param {float[]} [position] The position of the first bone of the arm.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @param {DuOCO.Location} [location=DuOCO.Location.FRONT] The location of the limb
 * @param {DuOCO.View} [view] The view
 * @returns {DuOCOLimb} The arm
 */
DuOCODoc.prototype.newArm = function( type, side, shoulder, arm, forearm, hand, claws, position, location, view )
{
    side = def(side, DuOCO.Side.LEFT);
    type = def(type, DuOCO.LimbType.HOMINOID);
    location = def(location, DuOCO.Location.FRONT);

    shoulder = def( shoulder, false );
    arm = def( arm, true );
    forearm = def( forearm, true );
    hand = def( hand, true );
    claws = def( claws, false );

    // Unit
    var u = this.height;

    // type
    var hum = type == DuOCO.LimbType.HOMINOID;
    var hum = type == DuOCO.LimbType.PLANTIGRADE && !claws;
    var plan = type == DuOCO.LimbType.PLANTIGRADE && claws;
    var dig = type == DuOCO.LimbType.DIGITIGRADE;
    var ung = type == DuOCO.LimbType.UNGULATE;
    var artF = type == DuOCO.LimbType.ARTHROPOD && location != DuOCO.Location.MIDDLE && location != DuOCO.Location.BACK;
    var artM = type == DuOCO.LimbType.ARTHROPOD && location == DuOCO.Location.MIDDLE;
    var artB = type == DuOCO.LimbType.ARTHROPOD && location == DuOCO.Location.BACK;
    if ( !hum && !plan && !dig && !ung && !artF && !artM && !artB ) hum = true;

    // View
    if (!isdef( view ))
    {
        if (type == DuOCO.LimbType.PLANTIGRADE && !claws) view = DuOCO.View.FRONT;
        else view = DuOCO.View.RIGHT;
    }

    // count how many bones we need
    var num = 1;
    if ( shoulder ) num++;
    if ( arm ) num++;
    if ( forearm ) num++;
    if ( hand ) num++;
    if ( claws ) num++;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.ARM, side, location, type );
    var b = limb.newArmature( i18n._("Arm"), num );

    // side
    var s = 1;
    if ( side == DuOCO.Side.RIGHT && view == DuOCO.View.FRONT ) s = -1;
    else if ( side == DuOCO.Side.LEFT && view == DuOCO.View.BACK ) s = -1;
    else if ( view == DuOCO.View.LEFT ) s = -1;  

    // shoulder position
    var x, y;
    var ox = 0;
    var oy = 0;
    if (hum)
    {
        x = s * u * .02;
        y = u * .78;
    }
    else if (plan)
    {
        x = s * u * .40;
        y = u * .92;
    }
    else if (dig)
    {
        x = s * u * .45;
        y = u * .86;
    }
    else if (ung)
    {
        x = s * u * .33;
        y = u * .80;
    }
    else if (artF)
    {
        x = s * u * .33;
        y = u * .65;
    }
    else if (artM)
    {
        x = s * u * .09;
        y = u * .50;
    }
    else if (artB)
    {
        x = s * u * -.03;
        y = u * .48;
    }

    // offset
    if (isdef( position ) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    if ( shoulder )
    {
        b.name = i18n._("Shoulder");
        b.type = DuOCO.Bone.CLAVICLE;
        b.x = x + ox;
        b.y = y + oy;
        if (b.children.length > 0) b = b.children[0];
    }
    if ( arm )
    {
        b.name = i18n._("Arm");
        b.type = DuOCO.Bone.HUMERUS;
        if (hum)
        {
            b.x = s * u * .12 + ox;
            b.y = u * .77 + oy;
        }
        else if (plan)
        {
            b.x = s * u * .48 + ox;
            b.y = u*.74 + oy;
        }
        else if (dig)
        {
            b.x = s * u * .45 + ox;
            b.y = u*.66 + oy;
        }
        else if (ung)
        {
            b.x = s * u * .35 + ox;
            b.y = u*.63 + oy;
        }
        else if (artF)
        {
            b.x = s * u * .24 + ox;
            b.y = u*.45 + oy;
        }
        else if (artM)
        {
            b.x = s * u * -.01 + ox;
            b.y = u*.44 + oy;
        }
        else if (artB)
        {
            b.x = s * u * -.17 + ox;
            b.y = u*.43 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    if ( forearm )
    {
        b.name = i18n._("Forearm");
        b.type = DuOCO.Bone.RADIUS;
        if (hum)
        {
            b.x = s * u * .15 + ox;
            b.y = u *.65 + oy;
        }
        else if (plan)
        {
            b.x = s * u * .36 + ox;
            b.y = u*.44 + oy;
        }
        else if (dig)
        {
            b.x = s * u * .21 + ox;
            b.y = u*.45 + oy;
        }
        else if (ung)
        {
            b.x = s * u * .24 + ox;
            b.y = u*.53 + oy;
        }
        else if (artF)
        {
            b.x = s * u * .55 + ox;
            b.y = u*.80 + oy;
        }
        else if (artM)
        {
            b.x = s * u * -.08 + ox;
            b.y = u*.82 + oy;
        }
        else if (artB)
        {
            b.x = s * u * -.44 + ox;
            b.y = u*.92 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    if ( hand )
    {
        b.name = i18n._("Hand");
        b.type = DuOCO.Bone.CARPUS;
        if (hum)
        {
            b.x = s * u * .12 + ox;
            b.y = u * .51 + oy;
        }
        else if (plan)
        {
            b.x = s * u * .38 + ox;
            b.y = u*.05 + oy;
        }
        else if (dig)
        {
            b.x = s * u * .45 + ox;
            b.y = u*.14 + oy;
        }
        else if (ung)
        {
            b.x = s * u * .19 + ox;
            b.y = u*.31 + oy;
        }
        else if (artF)
        {
            b.x = s * u * .77 + ox;
            b.y = u*.28 + oy;
        }
        else if (artM)
        {
            b.x = s * u * -.17 + ox;
            b.y = u*.42 + oy;
        }
        else if (artB)
        {
            b.x = s * u * -.77 + ox;
            b.y = u*.47 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    if ( claws )
    {
        b.name = i18n._("Claws");
        b.type = DuOCO.Bone.FINGER;
        if (plan)
        {
            b.x = s * u * .455 + ox;
            b.y = u*.035 + oy;
        }
        else if (dig)
        {
            b.x = s * u * .49 + ox;
            b.y = u*.03 + oy;
        }
        else if (ung)
        {
            b.x = s * u * .19 + ox;
            b.y = u*.14 + oy;
        }
        else if (artF)
        {
            b.x = s * u * .86 + ox;
            b.y = u*.07 + oy;
        }
        else if (artM)
        {
            b.x = s * u * -.25 + ox;
            b.y = u*.07 + oy;
        }
        else if (artB)
        {
            b.x = s * u * -.98 + ox;
            b.y = u*.07 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    // Tip
    b.name = i18n._("Arm") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    if (hum)
    {
        b.x = s * u * .08 + ox;
        b.y = u * .42 + oy;
    }
    else if (plan)
    {
        b.x = s * u * .585 + ox;
        b.y = 0 + oy;
    }
    else if (dig)
    {
        b.x = s * u * .59 + ox;
        b.y = u*.03 + oy;
    }
    else if (ung)
    {
        b.x = s * u * .21 + ox;
        b.y = oy;
    }
    else if (artF)
    {
        b.x = s * u * .93 + ox;
        b.y = oy;
    }
    else if (artM)
    {
        b.x = s * u * -.31 + ox;
        b.y = oy;
    }
    else if (artB)
    {
        b.x = s * u * -1.06 + ox;
        b.y = oy;
    }

    //Heel
    if (plan)
    {
        var heelBone = new DuOCOBone( i18n._("Heel") );
        heelBone.type = DuOCO.Bone.HEEL;
        heelBone.x = s * u * .38 + ox;
        heelBone.y = 0 + oy;
        b.children.push(heelBone);
    }

    return limb;
}

/**
 * Creates a new leg.
 * @param {DuOCO.LimbType} [type=DuOCO.LimbType.HOMINOID] The type of limb
 * @param {DuOCO.Side} [side=DuOCO.Side.LEFT] The side
 * @param {Boolean} [thigh=true]  Whether to create a thigh
 * @param {Boolean} [calf=true]  Whether to create a calf
 * @param {Boolean} [foot=true]  Whether to create a foot
 * @param {Boolean} [claws=false]  Whether to add claws
 * @param {float[]} [position] The position of the first bone of the arm.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @param {DuOCO.Location} [location=DuOCO.Location.BACK] The location of the limb
 * @param {DuOCO.View} [view] The view
 * @returns {DuOCOLimb} The leg
 */
DuOCODoc.prototype.newLeg = function( type, side, thigh, calf, foot, claws, position, location, view )
{
    side = def(side, DuOCO.Side.LEFT);
    type = def(type, DuOCO.LimbType.HOMINOID);
    location = def(location, DuOCO.Location.BACK);

    thigh = def( thigh, true );
    calf = def( calf, true );
    foot = def( foot, true );
    claws = def( claws, false );

    // Unit
    var u = this.height;

    // type
    var plan = type == DuOCO.LimbType.PLANTIGRADE;
    var dig = type == DuOCO.LimbType.DIGITIGRADE;
    var ung = type == DuOCO.LimbType.UNGULATE;
    var hum = type == DuOCO.LimbType.HOMINOID;

    // View
    if (!isdef( view ))
    {
        if (type == DuOCO.LimbType.PLANTIGRADE) view = DuOCO.View.FRONT;
        else view = DuOCO.View.RIGHT;
    }

    // count how many bones we need
    var num = 1;
    if ( thigh ) num++;
    if ( calf ) num++;
    if ( foot ) num++;
    if ( claws ) num++;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.LEG, side, location, type );
    var b = limb.newArmature( i18n._("Leg"), num );

    // side
    var s = 1;

    if ( side == DuOCO.Side.RIGHT && view == DuOCO.View.FRONT ) s = -1;
    else if ( side == DuOCO.Side.LEFT && view == DuOCO.View.BACK ) s = -1;
    else if ( view == DuOCO.View.LEFT ) s = -1;

    // thigh position
    var x, y;
    var ox = 0;
    var oy = 0;
    if (plan || hum)
    {
        x = s * u * .04;
        y = u * .53;
    }
    else if (dig)
    {
        x = s * u * -.45;
        y = u * .73;
    }
    else if (ung)
    {
        x = s * u * -.26;
        y = u * .69;
    }

    // offset
    if (isdef( position ) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    if ( thigh )
    {
        b.name = i18n._("Thigh");
        b.type = DuOCO.Bone.FEMUR;
        b.x = x + ox;
        b.y = y + oy;
        if (b.children.length > 0) b = b.children[0];
    }
    if ( calf )
    {
        b.name = i18n._("Calf");
        b.type = DuOCO.Bone.TIBIA;
        if (plan || hum)
        {
            b.x = s * u * .05 + ox;
            b.y = u*.28 + oy;
        }
        else if (dig)
        {
            b.x = s * u * -.16 + ox;
            b.y = u*.54 + oy;
        }
        else if (ung)
        {
            b.x = s * u * -.2 + ox;
            b.y = u*.52 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    if ( foot )
    {
        b.name = i18n._("Foot");
        b.type = DuOCO.Bone.TARSUS;
        if (plan || hum)
        {
            b.x = s * u * .036 + ox;
            b.y = u*.027 + oy;
        }
        else if (dig)
        {
            b.x = s * u * -.5 + ox;
            b.y = u*.24 + oy;
        }
        else if (ung)
        {
            b.x = s * u * -.38 + ox;
            b.y = u*.33 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    if ( claws )
    {
        b.name = i18n._("Claws");
        if (hum) b.name = i18n._("Toes");
        b.type = DuOCO.Bone.TOE;
        if (plan || hum)
        {
            b.x = s * u * .10 + ox;
            b.y = u*.0125 + oy;
        }
        else if (dig)
        {
            b.x = s * u * -.37 + ox;
            b.y = u*.03 + oy;
        }
        else if (ung)
        {
            b.name = i18n._("Hoof");
            b.x = s * u * -.37 + ox;
            b.y = u*.08 + oy;
        }
        if (b.children.length > 0) b = b.children[0];
    }
    // Tip
    b.name = i18n._("Leg") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    if (plan || hum)
    {
        b.x = s * u * .145 + ox;
        b.y = oy;
    }
    else if (dig)
    {
        b.x = s * u * -.25 + ox;
        b.y = oy;
    }
    else if (ung)
    {
        b.x = s * u * -.34 + ox;
        b.y = oy;
    }

    //Heel
    if ((plan || hum) && claws)
    {
        var heelBone = new DuOCOBone( i18n._("Heel") );
        heelBone.type = DuOCO.Bone.HEEL;
        heelBone.x = s * u * .01 + ox;
        heelBone.y = oy;
        b.children.push(heelBone);
    }

    return limb;
}

/**
 * Creates a new spine.
 * @param {Boolean} [head=true]  Whether to create a head
 * @param {int} [neck=1] Number of neck bones
 * @param {int} [spine=2] Number of spine bones
 * @param {Boolean} [hips=true]  Whether to create hips
 * @param {float[]} [position] The position of the first bone of the arm.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @param {DuOCO.View} [view] The view
 * @returns {DuOCOLimb} The spine
 */
DuOCODoc.prototype.newSpine = function( head, neck, spine, hips, position )
{
    head = def( head, true );
    neck = def( neck, 1 );
    spine = def( spine, 2 );
    hips = def( hips, false );

    // Unit
    var u = this.height;

    // count how many bones we need
    var num = 1;
    if ( head ) num++;
    num += neck;
    num += spine;
    if ( hips ) num++;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.SPINE, DuOCO.Side.NONE, DuOCO.Location.NONE );
    var b = limb.newArmature( i18n._("Spine"), num );

    // hips position
    var ox = 0;
    var oy = 0;
    var x = 0;
    var y = u * .53;

    // offset
    if (isdef( position ) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    if ( hips )
    {
        b.name = i18n._("Hips");
        b.type = DuOCO.Bone.HIPS;
        b.x = x + ox;
        b.y = y + oy;
        if (b.children.length > 0) b = b.children[0];
    }
    if (spine > 1)
    {
        var totalSpineLength = u*.27;
        var torsoLength = totalSpineLength*.5;
        var spineLength = (totalSpineLength - torsoLength) / (spine-1);
        var spineStart = u*.56;
        for (var i = 0; i < spine-1; i++)
        {
            b.name = i18n._("Spine");
            b.type = DuOCO.Bone.SPINE;
            b.y = spineStart + i*spineLength + oy;
            b.x = ox;
            if (b.children.length > 0) b = b.children[0];
        }
        // Torso
        b.name = i18n._("Torso");
        b.type = DuOCO.Bone.TORSO;
        b.y = spineStart + (spine-1)*spineLength + oy;
        b.x = ox;
        if (b.children.length > 0) b = b.children[0];
    }
    else if (spine == 1)
    {
        // Torso
        b.name = i18n._("Torso");
        b.type = DuOCO.Bone.TORSO;
        b.x = ox;
        b.y = u*.56 + oy;
        if (b.children.length > 0) b = b.children[0];
    }
    if (neck > 0)
    {
        var neckLength = u*.06 / neck;
        var neckStart = u*.83;
        for (var i = 0; i < neck; i++)
        {
            b.name = i18n._("Neck");
            b.type = DuOCO.Bone.NECK;
            b.y = neckStart + i*neckLength + oy;
            b.x = ox;
            if (b.children.length > 0) b = b.children[0];
        }
    }  
    if ( head )
    {
        b.name = i18n._("Head");
        b.type = DuOCO.Bone.SKULL;
        b.x = ox;
        b.y = u*.89 + oy;
        if (b.children.length > 0) b = b.children[0];
    }

    // Tip
    b.name = i18n._("Spine") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    b.x = ox;
    b.y = u + oy;

    return limb;
}

/**
 * Creates a new tail.
 * @param {int} [numBones=3] Number of tail bones
 * @returns {DuOCOLimb} The spine
 */
DuOCODoc.prototype.newTail = function( numBones )
{
    numBones = def( numBones, 3 );

    // Unit
    var u = this.height;

    // count how many bones we need
    var num = numBones+1;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.TAIL, DuOCO.Side.NONE, DuOCO.Location.NONE );
    var b = limb.newArmature( i18n._("Tail"), num );

    // for each part, adjust name and type
    for (var i = 0, n = num-1; i < n; i++)
    {
        b.name = i18n._("Tail");
        if ( i <= (n-1) / 3) b.type = DuOCO.Bone.TAIL_ROOT;
        else if ( i <= (n-1)*2 / 3) b.type = DuOCO.Bone.TAIL_MID;
        else b.type = DuOCO.Bone.TAIL_END;
        
        if (b.children.length > 0) b = b.children[0];
    }

    b.name = i18n._("Tail") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;

    return limb;
}

/**
 * Creates a new hair strand.
 * @param {int} [numBones=3] Number of hair bones
 * @returns {DuOCOLimb} The hair
 */
DuOCODoc.prototype.newHairStrand = function( numBones )
{
    numBones = def( numBones, 3 );

    // Unit
    var u = this.height;

    // count how many bones we need
    var num = numBones+1;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.HAIR, DuOCO.Side.NONE, DuOCO.Location.NONE );
    var b = limb.newArmature( i18n._("Hair"), num );

    var l = 100 / numBones;

    // for each part, adjust name and type
    for (var i = 0, n = num-1; i < n; i++)
    {
        b.name = i18n._("Hair");
        if ( i <= (n-1) / 3) b.type = DuOCO.Bone.HAIR_ROOT;
        else if ( i <= (n-1)*2 / 3) b.type = DuOCO.Bone.HAIR_MID;
        else b.type = DuOCO.Bone.HAIR_END;

        b.x = 0;
        b.y = 150 - i*l;
        
        if (b.children.length > 0) b = b.children[0];
    }

    b.name = i18n._("Hair") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    b.x = 0;
    b.y = 50;

    return limb;
}

/**
 * Creates a new wing.
 * @param {DuOCO.Side} [side=DuOCO.Side.LEFT] The side
 * @param {Boolean} [arm=true]  Whether to create an arm / humerus
 * @param {Boolean} [forearm=true]  Whether to create a forearm
 * @param {Boolean} [hand=true]  Whether to create a hand
 * @param {int} [feathers=5]  Number of feathers
 * @param {float[]} [position] The position of the first bone of the arm.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @param {DuOCO.View} [view=DuOCO.View.TOP] The view
 * @returns {DuOCOLimb} The wing
 */
DuOCODoc.prototype.newWing = function( side, arm, forearm, hand, feathers, position, view )
{
    side = def(side, DuOCO.Side.LEFT);
    view = def(view, DuOCO.View.TOP);

    arm = def( arm, true );
    forearm = def( forearm, true );
    hand = def( hand, true );
    feathers = def( feathers, 5 );

    // Unit
    var u = this.height;

    // count how many bones we need
    var num = 1;
    if (arm) num++;
    if (forearm) num++;
    if (hand) num++;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.WING, side, DuOCO.Location.NONE );
    var b = limb.newArmature( i18n._("Wing"), num );

    // side
    var s = 1;
    if ( side == DuOCO.Side.RIGHT && (view == DuOCO.View.TOP || view == DuOCO.View.BACK) ) s = -1;
    else if ( side == DuOCO.Side.LEFT && (view == DuOCO.View.BOTTOM || view == DuOCO.View.FRONT) ) s = -1;
    else if ( view == DuOCO.View.LEFT ) s = -1;

    // arm position
    var x, y;
    if ( view == DuOCO.View.TOP || view == DuOCO.View.BOTTOM )
    {
        x = s * u * -.12;
        y = u * .64;
    }
    else return limb; // not supported yet
    var ox = 0;
    var oy = 0;

    // offset
    if (isdef( position ) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // We need to keep the parents of the feathers
    var parent1, parent2;

    // for each part, set the location
    if ( arm )
    {
        b.name = i18n._("Arm");
        b.type = DuOCO.Bone.HUMERUS;
        b.x = x + ox;
        b.y = y + oy;

        if (!forearm && !hand)
        {
            parent1 = b;
            parent2 = b;
        }

        if (b.children.length > 0) b = b.children[0];
    }
    if (forearm)
    {
        b.name = i18n._("Forearm");
        b.type = DuOCO.Bone.RADIUS;
        b.x = s * u * -.22 + ox;
        b.y = u * .53 + oy;

        parent1 = b;
        if (!hand) parent2 = b;

        if (b.children.length > 0) b = b.children[0];
    }
    if (hand)
    {
        b.name = i18n._("Hand");
        b.type = DuOCO.Bone.CARPUS;
        b.x = s * u * -.37 + ox;
        b.y = u * .64 + oy;

        parent2 = b;
        if (!forearm) parent1 = b;

        if (b.children.length > 0) b = b.children[0];
    }

    // Tip
    b.name = i18n._("Wing") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    b.x = s * u * -.53 + ox;
    b.y = u * .66 + oy;

    // Add Feathers
    if (feathers == 0) return limb;

    function createFeather( xr, yr, xt, yt, p )
    {
        var boneName = DuString.generateUnique( i18n._("Feather"), names);
        b = new DuOCOBone( boneName );
        b.type = DuOCO.Bone.FEATHER;
        b.x = s * u * xr + ox;
        b.y = u * yr + oy;

        // tip
        var boneName = DuString.generateUnique( i18n._("Feather") +
            '_' + i18n._("Tip"), names);
        var bt = new DuOCOBone( boneName );
        bt.type = DuOCO.Bone.TIP;
        bt.x = s * u * xt + ox;
        bt.y = u * yt + oy;


        bt.attached = true;

        b.children.push(bt);
        p.children.push(b);
    }

    var names = [];
    // Single one, add in the middle
    if (feathers == 1)
    {
        createFeather( -.38, .63, -.56, .28, parent1 );
        return limb;
    }

    for (var i = 0, n = feathers; i < n; i++)
    {
        if ( i < n/2)
        {
            var x = DuMath.linear( i, 0, n/2, -.54, -.38);
            var y = DuMath.linear( i, 0, n/2, .66, .63);
            var xt = DuMath.linear( i, 0, n/2, -.96, -.56);
            var yt = DuMath.linear( i, 0, n/2, .62, .28);
            createFeather( x, y, xt, yt, parent2 );
        }
        else 
        {
            var x = DuMath.linear( i, n/2, n-1, -.38, -.22);
            var y = DuMath.linear( i, n/2, n-1, .63, .51);
            var xt = DuMath.linear( i, n/2, n-1, -.56, -.08);
            var yt = DuMath.linear( i, n/2, n-1, .28, .39);
            createFeather( x, y, xt, yt, parent1 );
        }
    }

    return limb;
}

/**
 * Creates a new snake spine.
 * @param {Boolean} [head=true]  Whether to create a head
 * @param {int} [spine=5] Number of spine bones
 * @param {float[]} [position] The position of the first bone of the arm.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @returns {DuOCOLimb} The snake spine
 */
DuOCODoc.prototype.newSnakeSpine = function( head, spine, position )
{
    head = def( head, true );
    spine = def( spine, 5 );

    // Unit
    var u = this.height;

    // count how many bones we need
    var num = spine+1;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.SNAKE_SPINE, DuOCO.Side.NONE, DuOCO.Location.NONE );
    var b = limb.newArmature( i18n._("Spine"), num );

    // hips position
    var ox = 0;
    var oy = 0;
    var x = u *.75;
    var y = u * .5;

    // offset
    if (isdef( position ) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    var spineLength = u*1.5 / spine;
    for (var i = 0; i < spine; i++)
    {
        b.name = i18n._("Spine");
        if ( i <= (spine-1) / 3) b.type = DuOCO.Bone.SNAKE_SPINE_ROOT;
        else if ( i <= (spine-1)*2 / 3) b.type = DuOCO.Bone.SNAKE_SPINE_MID;
        else b.type = DuOCO.Bone.SNAKE_SPINE_END;
        b.y = y + oy;
        b.x = x - spineLength*i - ox;
        if (b.children.length > 0) b = b.children[0];
    }

    // Tip
    b.name = i18n._("Spine") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    b.x = -u *.75;
    b.y = y + oy;

    // Head
    if (head)
    {
        var b = new DuOCOBone( i18n._("Head") );
        b.type = DuOCO.Bone.SKULL;
        b.x = x;
        b.y = y;

        var s = limb.armature.pop();

        limb.armature.push(b);
        b.children.push(s);

        var bt = new DuOCOBone( i18n._("Head") +
            '_' + i18n._("Tip") );
        bt.type = DuOCO.Bone.SKULL_TIP;
        bt.x = x + u*.2;
        bt.y = y;
        bt.attached = true;

        b.children.push(bt);
    }

    return limb;
}

/**
 * Creates a new fish spine.
 * @param {Boolean} [head=true]  Whether to create a head
 * @param {int} [spine=3] Number of spine bones
 * @param {float[]} [position] The position of the first bone of the spine.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @returns {DuOCOLimb} The fish spine
 */
DuOCODoc.prototype.newFishSpine = function( head, spine, position )
{
    head = def( head, true );
    spine = def( spine, 3 );

    // Unit
    var u = this.height;

    // count how many bones we need
    var num = spine+1;

    if (num == 1) return null;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.FISH_SPINE, DuOCO.Side.NONE, DuOCO.Location.NONE );
    var b = limb.newArmature( i18n._("Spine"), num );

    // hips position
    var ox = 0;
    var oy = 0;
    var x = u *.75;
    var y = u * .5;

    // offset
    if (isdef( position ) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    var spineLength = u / spine;
    for (var i = 0; i < spine; i++)
    {
        b.name = i18n._("Spine");
        if ( i <= (spine-1) / 3) b.type = DuOCO.Bone.FISH_SPINE_ROOT;
        else if ( i <= (spine-1)*2 / 3) b.type = DuOCO.Bone.FISH_SPINE_MID;
        else b.type = DuOCO.Bone.FISH_SPINE_END;
        b.y = y + oy;
        b.x = x - spineLength*i - ox;
        if (b.children.length > 0) b = b.children[0];
    }

    // Tip
    b.name = i18n._("Spine") +
        '_' + i18n._("Tip");
    b.type = DuOCO.Bone.TIP;
    b.x = -u *.25;
    b.y = y + oy;

    // Head
    if (head)
    {
        var b = new DuOCOBone( i18n._("Head") );
        b.type = DuOCO.Bone.SKULL;
        b.x = x;
        b.y = y;

        var s = limb.armature.pop();

        limb.armature.push(b);
        b.children.push(s);

        var bt = new DuOCOBone( i18n._("Head") +
            '_' + i18n._("Tip") );
        bt.type = DuOCO.Bone.SKULL_TIP;
        bt.x = x + u*.3;
        bt.y = y;
        bt.attached = true;

        b.children.push(bt);
    }

    return limb;
}

/**
 * Creates a new fin.
 * @param {DuOCO.Side} [side=DuOCO.Side.LEFT] The side
 * @param {int} [fishbones=5]  Number of feathers
 * @param {float[]} [position] The position of the first bone of the arm.<br />
 * If omitted, computed automatically according to the current character in the doc.
 * @param {DuOCO.View} [view=DuOCO.View.RIGHT] The view
 * @returns {DuOCOLimb} The fin
 */
DuOCODoc.prototype.newFin = function( side, fishbones, view, position )
{
    side = def(side, DuOCO.Side.LEFT);
    view = def(view, DuOCO.View.RIGHT);

    fishbones = def( fishbones, 5 );

    // Unit
    var u = this.height;

    // Create limb
    var limb = this.newLimb( DuOCO.Limb.FIN, side, DuOCO.Location.NONE );

    // side
    var s = 1;
    if ( side == DuOCO.Side.RIGHT && (view == DuOCO.View.TOP || view == DuOCO.View.BACK) ) s = -1;
    else if ( side == DuOCO.Side.LEFT && (view == DuOCO.View.BOTTOM || view == DuOCO.View.FRONT) ) s = -1;
    else if ( view == DuOCO.View.LEFT ) s = -1;

    // arm position
    var x = s * u * -.17;
    var y = u * .47;
    var ox = 0;
    var oy = 0;

    // offset
    if ( isdef(position) )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // Create root
    var rootBone = new DuOCOBone( i18n._("Fin") );
    rootBone.type = DuOCO.Bone.FIN;
    rootBone.x = x + ox;
    rootBone.y = y + oy;
    limb.armature.push(rootBone);
    // Tip
    var bt = new DuOCOBone( i18n._("Fin") +
        '_' + i18n._("Tip") );
    bt.type = DuOCO.Bone.TIP;
    bt.x = s * u * -.24 + ox;
    bt.y = u * .62 + oy;
    bt.attached = true;
    rootBone.children.push(bt);

    // Add FishBones
    if (fishbones == 0) return limb;

    var names = [];

    function createFishbone( xr, yr, xt, yt )
    {
        var boneName = DuString.generateUnique( i18n._("Fishbone"), names);
        b = new DuOCOBone( boneName );
        b.type = DuOCO.Bone.FIN_FISHBONE;
        b.x = s * u * xr + ox;
        b.y = u * yr + oy;

        // tip
        var boneName = DuString.generateUnique( i18n._("Fishbone") +
            '_' + i18n._("Tip"), names);
        var bt = new DuOCOBone( boneName );
        bt.type = DuOCO.Bone.TIP;
        bt.x = s * u * xt + ox;
        bt.y = u * yt + oy;
        bt.attached = true;

        b.children.push(bt);
        rootBone.children.push(b);
    }

    // Single one, add in the middle
    if (fishbones == 1)
    {
        createFishbone( -.22, .43, -.77, .60 );
        return limb;
    }

    for (var i = 0, n = fishbones; i < n; i++)
    {
        var x = DuMath.linear( i, 0, n-1, -.24, -.15);
        var y = DuMath.linear( i, 0, n-1, .63, .41);
        var xt = DuMath.linear( i, 0, n-1, -.82, -.17);
        var yt = DuMath.linear( i, 0, n-1, .60, .29);
        createFishbone( x, y, xt, yt );
    }

    return limb;
}

/**
 * Converts a doc coordinate in centimeters to pixel coordinates
 * @param {float[]} point The coordinate to convert
 * @returns {int[]} The coordinates in pixels relative to the comp
 */
DuOCODoc.prototype.toPixels = function( point )
{
    // First, convert to pixels
    var x = point[0]*this.pixelsPerCm;
    // minus because in 2D apps, 0 is top
    var y = -point[1]*this.pixelsPerCm;
    // We're relative to the world, add its coordinates
    x += this.world[0];
    y += this.world[1];
    return [x, y];
}

/**
 * Converts a pixel coordinate in pixels to centimeters doc coordinates
 * @param {int[]} point The coordinate to convert
 * @returns {float[]} The coordinates in centimeters relative to the doc
 */
DuOCODoc.prototype.fromPixels = function( point )
{
    var x = point[0];
    var y = point[1];
    // We're relative to the world, remove its coordinates
    x -= this.world[0];
    y -= this.world[1];
    // Convert to cm
    x /= this.pixelsPerCm;
    // Y coordinate is at bottm in OCO, top in 2D docs
    y /= -this.pixelsPerCm;
    return [x, y];
}

/**
 * Creates a new OCO Limb.
 * @class
 * @classdesc A limb contained in an OCO Doc.
 * @param {DuOCO.Limb} [limb=DuOCO.Limb.CUSTOM] A Predefined limb
 * @param {DuOCO.Side} [side=DuOCO.Side.NONE] - The side of the limb
 * @param {DuOCO.Location} [location=DuOCO.Location.NONE] - The location of the limb
 * @param {DuOCO.LimbType} [type=DuOCO.LimbType.CUSTOM] - The type of the limb
 * @category DuOCO
 */
function DuOCOLimb(limb, side, location, type )
{
    /**
     * The predefined limb
     * @name limb
     * @memberof DuOCOLimb
     * @type {DuOCO.Limb}
     */
    this.limb = def(limb, DuOCO.Limb.CUSTOM);
    /**
     * The type of the limb
     * @name type
     * @memberof DuOCOLimb
     * @type {DuOCO.LimbType}
     */
    this.type = def(type, DuOCO.LimbType.CUSTOM);
    /**
     * The sided of the limb
     * @name side
     * @memberof DuOCOLimb
     * @type {DuOCO.Side}
     */
    this.side = def( side, DuOCO.Side.NONE);
    /**
     * The location of the limb
     * @name location
     * @memberof DuOCOLimb
     * @type { DuOCO.Location}
     */
    this.location = def( location, DuOCO.Location.NONE);
    /**
     * The bones of the limb
     * @name location
     * @memberof DuOCOLimb
     * @type {DuOCOBone[]}
     */
    this.armature = [];
}

/**
 * Gets the boundaries of the limb
 * @returns {float[]} [left, top, right, bottom]
 */
DuOCOLimb.prototype.bounds = function( )
{
    return DuOCO.getBounds( this.armature );
}

/**
 * Creates a new chain of bones and adds it to the limb.
 * @param {string} name The name of the bones (will increment if needed)
 * @param {int} [num=2] The number of bones in the chain
 * @param {float} [length=100.0] The length in centimeters
 * @return {DuOCOBone} The root bone.
 */
DuOCOLimb.prototype.newArmature = function( name, num, length )
{
    num = def(num, 2);
    length = def(length, 100.0);
    var boneLength = length / (num-1);

    var x = -length / 2;
    var b = null;
    var root = null;
    var names = [];

    for (var i = 0; i < num; i++)
    {
        var boneName = DuString.generateUnique(name, names);
        var bone = new DuOCOBone( boneName );
        names.push(boneName);
        bone.y = 100;
        bone.x = x;

        if (b != null)
        {
            b.children.push( bone );
            bone.attached = true;
        }
        else
        {
            this.armature.push( bone );
            root = bone;
        }

        x += boneLength;
        b = bone;
    }

    return root;
}

/**
 * Creates a new OCO Bone.
 * @class
 * @classdesc A bone contained in an OCO Limb.
 * @param {string} name The name
 * @category DuOCO
 */
function DuOCOBone(name)
{
    /**
     * The name of the bone
     * @name name
     * @memberof DuOCOBone
     * @type {string}
     */
    this.name = name;
    /**
     * The x coordinate of the bone
     * @name x
     * @memberof DuOCOBone
     * @type {float}
     */
    this.x = 0.0;
    /**
     * The y coordinate of the bone
     * @name y
     * @memberof DuOCOBone
     * @type {float}
     */
    this.y = 0.0;
    /**
     * true if this bone is attached to its parent.
     * @name attached
     * @memberof DuOCOBone
     * @type {Boolean}
     */
    this.attached = false;
    /**
     * The child bones.
     * @name children
     * @memberof DuOCOBone
     * @type {DuOCOBone[]}
     */
    this.children = [];
    /**
     * The child limbs.
     * @name limbs
     * @memberof DuOCOBone
     * @type {DuOCOLimb[]}
     */
    this.limbs = [];
    /**
     * The type of bone.
     * @name type
     * @memberof DuOCOBone
     * @type {DuOCO.Bone}
     */
    this.type = DuOCO.Bone.CUSTOM;
}

/**
 * Translates the bone by [x, y] pixels
 * @param {int} [x=0] The horizontal offset
 * @param {int} [y=0] The vertical offset
 * @param {bool} [translateChildren=true] If false, the children stay at their current location
 */
DuOCOBone.prototype.translate = function( x, y, translateChildren ) {
    translateChildren = def(translateChildren, true);
    x = def(x, 0);
    y = def(y, 0);

    this.x += x;
    this.y += y;

    if (translateChildren) {
        for (var i = 0, n = this.children.length; i < n; i++) {
            this.children[i].translate(x, y);
        }
    }
}

/**
 * Translates the bone to the new coordinates
 * @param {int} [x] The new X value. If omitted, moves the layer vertically
 * @param {int} [y] The new Y value. If omitted, moves the layer horizontally
 * @param {bool} [translateChildren=true] If false, the children stay at their current location
 */
DuOCOBone.prototype.translateTo = function( x, y, translateChildren ) {
    translateChildren = def(translateChildren, true);
    x = def(x, this.x);
    y = def(y, this.y);

    var offsetX = x - this.x;
    var offsetY = y - this.y;

    this.x = x;
    this.y = y;

    if (translateChildren) {
        for (var i = 0, n = this.children.length; i < n; i++) {
            this.children[i].translate(offsetX, offsetY);
        }
    }
}//*/
// ==================== |-------| ====================
// ==================== | icons | ====================
// ==================== |-------| ====================

// ==================== |------------| ====================
// ==================== | w12_layers | ====================
// ==================== |------------| ====================

var w12_layers = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u008B\x00\x00\x05\u008B\x01\x12\u0096^s\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01)IDAT(\u0091\u008D\u0091;J\x03\x01\x14E\u00CF\u009D1!\u008Dv\u0082)\u00D4UX\u00EA\n2\u00DA\rb\u00A7 Q\u0083(\u00B8\x07\u00B1\u00F1\u0097X\x18t\x01\u0092*\u00D3Lo\u00A9\u00EB\x10\u008C\x10\x14\u00D1&\u00930\u00D7B\x03Q\u00FC\u009D\u00F2\u00BDS\x1C\u00B8b\u0084f\u00B3Y(\u0097\u00CB\u00CB\u00B6w%\u00D9\u00F6q\u00A7\u00D3iU\u00AB\u00D5\u00FE\u00D0\x11@\u0092$\u00E3\u00C0\x1A\u00B0\x07L\u00F3\u0099\x0E\u00D0\f\u00C3\u00F0\u00B4R\u00A9<)I\u0092}\u00A0\x06L\u00F0;\u00CF@=\u00B0]\x00\u0082?d\u0080PRA\x00i\u009AN\x0E\x06\u0083u\u00DB\u00DB\u00C0\u00D4\x17\u00F1\u00DEv=\f\u00C3\u00CB(\u008A\u00BA\u00C3\u00A4M\u00E0:\b\u0082F\u009E\u00E7\u00B3\u00C0\x0E`\u00DB\u00A7\u0092\u00EE$m\u00DB\u009E\x07\x1Ac@\x11(\x00Ky\u009E/\x017\u00B6\x0F\u00F5N\r\u0098\u00B3\r\u00F0j\u00BB\u00F8\u00AF$\u00A0\x11\x04\u00C1E\x14E]\u00B5\u00DB\u00ED\x03I\x1B\u00C0\u00B5\u00ED\u0086\u00A4\u0099\x1F\u0092\x16$\u009D\u008DI\x12\x10\x02\u008B\u0092\x16%\u00DD\u00DA>\u00FA\u00B8o\u008D$\u00BD\u00C0\u00C7pi\u009AN\u00F4\u00FB\u00FDU\u00BE\x1F\u00EE\x018\u00EF\u00F5z\u00F58\u008E\x1F5\u00FAi\u00B5Z\u00C5R\u00A9\u00B4b{\u0098t\u0092e\u00D9U\x1C\u00C7\u00D9\u00D0y\x032\x1D\u0095\t\x05\u00F5H\u00C0\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_layers.png", "w12" );
w12_layers;
// ==================== |----------| ====================
// ==================== | w12_comp | ====================
// ==================== |----------| ====================

var w12_comp = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00\x00\f\b\x06\x00\x00\x00Vu\\\u00E7\x00\x00\x00\tpHYs\x00\x00\x05\u0089\x00\x00\x05\u0089\x01mh\u009D\u00FA\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\x1AIDAT(\u0091\u0095\u00D0\u00A1O\x03Q\f\u00C7\u00F1o\u00DF&\u00B6\u0090\x10\x12<\t(\u0082\u00C0\u0080`\b\x14\u008E\u00DC\u00DBC\u0092%C\u00C1\x7FA2\u0081\u00C7#\x10\x18\f\u00A2\u00EF0\b\ffCm\x0E\u0089@\u0080a\x0Bd\x01\u00CC\u00B8b\u00EE\x16\u00C4NPS\u00D1O\u009B\u00E6\x07\u00FF,Q\u00D5$\u0084p\u0093\u00A6\u00E9q\u0096e+\u00B3\u0090s\u00EE\u00C9{\x7F\u00AE\u00AA\u0089\u00C4\x18\u00BFD\u00E4\u00D1\u00CC\u00D6\u0080z\u00C9\u00E1\u00EF\u00C28\u00A0\u00EF\u00BD\u00DF\x04\u00FA\u00C0;\u00F03caj$\u00C6\u00F8bf\u0097\"\u00D2\u00AAT*\u00EBY\u0096\x1D\u009A\u00D9Y\x0E\u0087\u00C0\"\u00F0\u009A\u009B\u00B6\x03F\"r\r|L&\u0093S3k\u00E5\u00F8\x1EX\x06.\u00FE\u0098\u0091\x13\x11gf\x0B\"\u00E2Dd\x158\x01\u00D2Z\u00AD\u00B6\u00D7l6\u00C7\u0083\u00C1\u00E0\b\x18\x16\u00A6jf\u00F3\u00CE\u00B9\u008D\u00BC\u00EF$I\u00F2\x06\u00DC\x16\u00CFw:\u009D,\u00C6X/\u008C\u00A8j7\u0084\u00B0\u00AD\u00AA]\x11i\u00CC\u008A\u00C8\u00CCzS\x13c\x1C\x03\x0F@\x03\u0098+\u0089\u00F5\x13\u00E8\x01[U3\u00DB\x0F!\u00DC\u00A5iz\x00,\u0095,<{\u00EF\u00AFTu\u00B7d^^\u00BFa\u00FB\u0082\u00E8+B\u00D9\u0091\x00\x00\x00\x00IEND\u00AEB`\u0082", "w12_comp.png", "w12" );
w12_comp;

// ==================== |--------------------| ====================
// ==================== | w16_selected_props | ====================
// ==================== |--------------------| ====================

var w16_selected_props = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\u00DDIDAT8\u008D\u00AD\u00921H\u00D5a\x14\u00C5\x7F\u00E7\u00F3\u00FF\u00CF7\u00884444\u00D6P \u00C8S\bI\u00C8I\x1E\u0084|\u00F0\u0096\u00DA\u00A2hn\x11iT\u00A7\u0086hqQJ\u0087\x02)\b\u00F1}\u00EFU\u00D4P[4D\u00BC\u00A50P\"B\u008A\x1A\x04\u00B5\u00C2\u00C7\u00E3\u00DD\u00DB\u0092\"\u00F6\u00B4\u00A0\u00CEt\u00E1\u009Es\u00EE\u00E5\u00DC\x0B\u00FF\b\u00FD\u0089\u0090RJ\u00C0\u00A1\x10\u00C2\u00D5\u0091\u0091\u0091e\u0080\u0085\u0085\u0085cY\u0096\u00CD\x00\u009D\u00D9^A\u00ADV\x1Bt\u00F7S\u00EE\u00FE<\u00C6\u00B8\x02\f\x01\u00DD\u00ADV\u00AB\b,\x03\u00E4y\u00DE\u00E3\u00EE\u00E7\x00~30\u00B3\u00A7\u00C0\x13\u00E0\fpqgUi4\u00A5\u00F4M\u0092\u00B9\u00FB\x180'\u00E9\u008D*\u0095\u00CA\u0094\u00A4\x01\u00E0V\u008C\u00F1vJ\u00E9{\u00AB\u00D5\u00EA\u00EF\u00E8\u00E8H@\x1F\u00B0\nt\u00EF\u00993\x17c\u00BC\x02\x10$]\x06n\x02\u00E5\u00EDn\u00B9\\^\x02^\u00BA\u00FBy\u00C0\u00DBD\u00B3\u00B6]d\x00\u0085B\u00A1\u00B6\u00B5\u00B5u\u00BDR\u00A9\u009C\u00DEE\u00FA\x02t\u00B6\x11\x03\u0094\u00AA\u00D5\u00EAt\u0096e\u009F\x02\u00C0\u00F0\u00F0\u00F0\x0F\u00E0\u008E\u00A4\u00B3\u00FB\b\u00F6\u00A2\u00C7\u00DD\u00DF7\u009B\u00CD\u00D5\u00F0\x17\u00E4\u0083N}\u00A4\u009D\u0081\x16\x17\x17\x0FK\u00FA*\u00A9hf1\u00CF\u00F3!IK\u00BB8\x1F\u0080A3\u00EB\u00CF\x00\u009F\u009C\u009CToo\u00AF\u0085\x10df\u008FC\buw\u00DF\x04\u00DE\u0085\x10f\u009B\u00CDf\x00^\x01'\x7F\x19\u00BC\u008D1\u00BE\x00\b\u0080\x17\u008B\u00C5\u00B1\x10B\u00C3\u00DD/Iz-\u00E9\x1A\u00B0&\u00E9F\u008C\u00F18\u0090\x03S\u00C0<p\u00CF\u00CC\u00C6w\u00D6\u00ADV\u00AB\u00D1\u00CC\x06\u0080\x0B\u0092\u00C6%\x1Du\u00F7Q\u00E0\u00AE\u00A4>w\u00FF\b\f\u00D6\u00EB\u00F5\x13\x13\x13\x13\u00B6o@)\u00A5\x07!\u0084\u00F9\u00AE\u00AE\u00AE\u0087\u00EB\u00EB\u00EB+\u0085Ba\u00A0\u00D1h\x14C\b\u00B9\u0099=\u008B1n\u00B6Kq\u00E7\u0095\u00DD}\u00D6\u00CCf666\u00EEK\u009A.\u0095J\u009F\u0081G\x07\\\u00E0\u00FF\u00E0'\u00AF\u00D4\u00BA\x00\u009AB\u00FF\u00EA\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_selected_props.png", "w16" );
w16_selected_props;
// ==================== |------------------| ====================
// ==================== | w16_compositions | ====================
// ==================== |------------------| ====================

var w16_compositions = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\u00E8IDAT8\u008D\u00A5\u0092?hSQ\x14\u00C6\x7F\u00E7\u00E6\u00D5\u00D4,U\x07\u0087((\u00EE.\u0085N\u008A\u00A5\u008BP\u00F1\u00BD\u00C4\u00A1\u00A1E\x10\x1D\x14\x04A\u00D1\u00AD[u\u00F0\x0F\x15,.nR\x14\u00FF,67!H\u00A7\u00EA\u00E2\"v\u00F2\u00CF n\u00E2R\x10E\u009A\u009A\u0084\u00FB>\x07\u00D3\u00F0Z\x1B\x07=p\u0087\u00EF\u00DEs\u00BE\u00F3\u009D\u00EF\x1E\u00F8\u00CF\u00B0,h4\x1A\u00FB:\u009D\u00CE9 \u00B79\u00D19'\u00E0Y\u0092$\u00AF\u00FB\x12\u00D4\u00EB\u00F5\u00E14M_\x01\u00F9>\r\u00BF\u0099\u00D9\u00E1$I\u00DE\u00F5\u0088\u00B3\u00AFq\x1C/\x03\u00A7\x01mQ<-\u00E9\u00A6\u00A4\u00BA\u00F7\u00BE\u00B8AA\u00ADV\x1B\x01\u00AEHr\u0092\u00AE9\u00E7\u008EI\u00BAafo\u0080\u00F3\u00C0-I\u00C3\u0092\u00AE\u009B\u00D9Y\u00E0;0Z*\u0095~\u00AC+\u00C8I:\x01L\u0098YC\u00D2\x03\u00E0\u009E\u00A4]!\u0084\u008FI\u0092\u008C\u0099\u00D9I\u00E7\u00DCn`\x16(\x02O\u0097\u0096\u0096\u00A2\u009E\x07\u00DE\u00FBI\u00E0\x11`\u0092\u00DE\u00B6\u00DB\u00ED\u00D1|>?/i\u009B\u00A4\u00E9\u00EC,\u00B9\\nH\u00D2c3[0\u00EF\u00FD!\u00A0\u00E8\u009C\u00FB\x14B8nf3\u00DD\u00BC\u00E7\u0085Ba\u00AA\u00D9l\u00CE\x01{\u00FA\u0098\u00BA\x12\x01O\u0080\u00BDi\u009A\u00AE:\u00E7\u00C6\u0080\u00FD\u0092\u00CE\x00\u00E3\u00CDf\u00F3>p'M\u00D3\u00D5l\u0095\u0099\u00AD\u0095\u00CB\u00E5\u00F7\x00V\u00AB\u00D5F$\u00BD\x00\n\u00C0\u008As\u00EEH\ba\u00CE\u00CC\u008E\u00F6\u00E9\n\u00BF\x7F\u00E9T\u00A9Tzh\x00\u00D5j56\u00B3\x05 gf\x1F:\u009D\u00CEx\x14Eu\u00E0\u00E0_H\u00D6\x06\x06\x06vfM\u00BC\f\u00DC\u00EE\u00C2\u0097Q\x14M\u00B6\u00DB\u00ED\u00DE\u00ECQ\x14m\x0F!\ff\u008C\u00FC\x1A\u00C7\u00F1\u00F2\u0086M\u00F4\u00DE\u00DF\x05.t\u00E1O\u00E0\u00CB\x1F\u00DA\u00A5\u00F9r\u00B9|u\x1Do\u00D8\u00C4V\u00ABu\u00C9\u00CC\u00EA]8\b\x1C\u00D8|\u00CCl\u00C6{?\u00D1S\u0096%\u00A8T*aqqq\u00AA\u00D5j]\u0094\u00B4\x03\x18\u00DAjx\u00E7\u00DC\u00E7\u00AD\u00EE\u00FF)~\x01\x06L\u00C4X\u00D7\x11K\x1B\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_compositions.png", "w16" );
w16_compositions;
// ==================== |---------------------------| ====================
// ==================== | w16_selected_compositions | ====================
// ==================== |---------------------------| ====================

var w16_selected_compositions = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x02\tIDAT8\u008D\u00A5S=hSQ\x18=\u00E7\u00DE\u0084<\x7F(\u00B8\u00B8\u00A8\u00A0\u0083N\u008A\x04T\n]\x12,\u00A6\x10|7\u0083VP\u00D0I\u008AJ\x05E\u0085\x16\x1D\x1C:U\x11D\u009C\u00DBA\u0090@\u00C8\u00BB\u00EF\u00E1\"\u0085*\u00B8T\u0091l\u00BA\u00B8\u00B8\x16\x04\u00B5\u0096\u00BC\u0094\u00E6\u00FB\\\u0092\u00F0\x1A\u00DAE\u00CF\u00F4q\u00BE\u00EF;\u00F7r\u00CE\u00BD\u00C0\x7F\u0082\u00FD\u00A2\u00D1h\x1C\u00B4\u00D6\u00DE\x00`\u00B7\x1D$\u00FF\x04A\u00F0\u00ACR\u00A9\u00ACg\u00F9\\\u00BF\u00B0\u00D6\x1E \u00F9 \u00CB\r#M\u00D3\u00D1\u00E5\u00E5\u00E5Z\u00B9\\\u00DE\u00ECs\u00A6_\u00D4j\u00B5\x15\u0092\u00B7v\u00D8]%y\t\u00C0\u0091\u00B5\u00B5\u00B5\x17\u00D9\u00C6@ \u008A\u00A2C\u00C6\u0098%U\u009D\u00CF\\{\n\u00C0{\x00\u00FB\x01\u008C\u0091\u009CV\u00D5)\u00EF\u00FD\u00CC\x16\u0081(\u008A.\u0090\\\x10\u0091\x19\u0092\u00A7\x00\u00BC\x06\u0080n\u00B7\u00FB\u00D69WR\u00D5PU\u00C7U\u00F5:\u0080o\x00\u00E6\u00A2(\u00BA\f\u00F4LL\u0092\u00E4\u00A8\u0088,\u0088H\u00D5Z{VD\u00AE\x00\u00D8g\u008C\u00D9\u009D\u00A6iirr\u00B2]\u00AF\u00D7-\u0080\u00BD\x00\u00DAA\x10\u00DCQ\u00D5\u00C7\"21H\u00A1\u00D9l\u0096\u008C1\u00B3\u00F9|\u00FE\u00FC\u00C6\u00C6\u00C6M\x00'H\u009E\x01\u00B0\x0B\u00C0\x0F\x00\u00BF\u0086|\x19\x05\u00F0\u009BQ\x14\u00CD\u00F5\x06\x7F\u00AA\u00EA\x12\u00801\u00E7\u00DC\u00B58\u008E\u009F\u00A8\u00EA\x1E\u0092\u00AB\u00D9-U]\x07\u00F0\u00B9\u00E7\u00D1*\u00BD\u00F7\u009F\u00BA\u00DDn\u00D5Z{\x12\u00C0]\x11Y\u00B1\u00D6\u00E6\u00C20|\u0098$\u00C99U=<$P\"\u00F9!\f\u00C3\u0097\x00\u00C08\u008EO\u00AB\u00EA|\x10\x04\u00D5v\u00BB\u00ED\u008C1U\u0092\u00A9\u0088|\x11\u0091w\u00C3y\u0092\u00CC\u0091\\\x1C\x19\x199^.\u00977\u00FB&\u00D6D\u00E4j\u00A7\u00D3\u00B9X(\x14\x1E\u00A9j\u0081\u00E4\u00F7\u00E1\u00D33\"_\u009Ds\u008B\u0083\x14zQ\u00DE&y,\f\u00C3i\u00EF\u00FD\u00AC1f\\U73{y\x00o\u009Cs\u00F3\u00C3\u0082\x03x\u00EF\u009F\u00C6q|o\u00BB\u009E\u00AA2\u008E\u00E3W\u00DE\u00FB\u0089,\u00BF\u00E5\u00DD\u00B7Z\u00AD\u00FB\u00C5b\u00F1\u00B9\u00F7\u00FE#I\u0093\u00ED%I\x02U\u00CD\u0091\u00DC\u00F1\u00AF\u00FC\x13\u00FE\x02\u00E5\u0087\u00DE.\u00BB\u0087\t\u00B3\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_selected_compositions.png", "w16" );
w16_selected_compositions;
// ==================== |------------| ====================
// ==================== | w16_layers | ====================
// ==================== |------------| ====================

var w16_layers = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x01\u00B0IDAT8\u008D\u009D\u00931\u008B\x13A\x14\u00C7\x7Fov\u0087\x1C.YI+\n\"\n'r\x1F\"\u00F6\u00CBb*\u0085;\u0089h)*'\u00D8_c\u00E7\x15\u00E1\u00B0H!9\u00B7PR\u00EC\u00B1\u0092~?\u00C4\u0081w\u009F\u00C0\u00E6\u00B40a\u00C5\u00B8{\u00FB,\x1C1Y\x02Q\u00FF0\u00C5\u00BCy\u00BF\u0099\u00C7\u009B\u00FF\x13V(\u00CF\u00F3\u008D\u00E9t\u00BA\u00A3\u00AAO\x00Dd\x10\u0086a\u00D2\u00EDv\u00BF7seq3\x1E\u008F/\u00B6Z\u00AD>\u00F0\x02\u00B8\u00D4\u00C8=\x03^\x1Bc\x0E\u00A2(\u00FA\u00BCt\u00C1d2\u00B9V\u00D7\u00F5SU}\b\x04\u00AB\u00AAZ\u00D0\\U\u00C7\u009E\u00E7\u00BD\u008C\u00A2\u00E8T\u00B2,;\x04\u00B6\x01\u00B3\x06l\u00AA\x06\x12\x03\u00E8?\u0082K2q\x1C\u00F7E\u00E4\u00BA\u0088\f\u0080\u00E2/\u0098\u00B9\u00AA&\u00C6\u0098[q\x1C\u00F7\r\u0080\u0088\u009C\u008B\u00C8\u0087\u00BA\u00AE/\x03\u00CF\u0080O+\u00C03`\u00CFZ{\u00C5\u00F3\u00BCC\u00E0\x1B\u0080dY6\x04\x1E\x00>p\f\u00BC\u00B2\u00D6\x1EUUuw\u00F1\x1B}\u00DF\x7F_\u0096\u00E5\x1D\u00E09\u00B0\x05T\u00C0\x1B#\"!\u00E0\u00B9W\u00B6\u0080QY\u0096'@\u00C7Z\u00DB\u00B5\u00D6v\u0081\u008E\u008B\u008D\\\x0E\u008E\t\x05 M\u00D3\u009B\u00BE\u00EF\u00EF\u00AA\u00EA\x0E\u00D0Z\u00D7\x03\x11I\u00AA\u00AA\u00DA\u00EF\u00F5z'\u00BF{\u00F0\x03H\u00E7\u00F3\u00F9U`\x0F\u00F8\u00B2\x02\u00FC*\"\x03U\u00BD\x01\u00A4\u008EA\u00B2,\x1B\u00F1\u00CB\x07\x1Ep\n\u00EC\x07A\u0090\x16Eq\x0Fx\u00EC\u00E0\u0083 \b\u00DE\x15E\u00D1\x03v\u0081M\u00E0\\U\x13\u00A3\u00AA\u0086?\u0096\u00DE\x04\u0086EQ|\x04:\u00C0m\u00B7:.6t9\u00AEp1M+?\x02.\u00AC\u00EB\u00C1\u0092\u0095\x17O\u00FE{\u0098\u009A\u00CA\u00F3|c6\u009B\u00DD_\u00F4A\u00BB\u00DD~\u00BBj\u009C\x7F\x023\u0080\u00B3\u00B4\u00B9\u00C7.\u0087\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_layers.png", "w16" );
w16_layers;
// ==================== |---------------------| ====================
// ==================== | w16_selected_layers | ====================
// ==================== |---------------------| ====================

var w16_selected_layers = new DuBinary( "\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\u00F3\u00FFa\x00\x00\x00\tpHYs\x00\x00\x07a\x00\x00\x07a\x01\u0095\u00C3\u00B8\u00B6\x00\x00\x00\x19tEXtSoftware\x00www.inkscape.org\u009B\u00EE<\x1A\x00\x00\x02bIDAT8\u008D\u0095\u0093=hTy\x14\u00C5\u00CF\u00B9\u00EFe\u00DE\u00AC\u0085!\u00A2\u0090\u00C8\x16b\u00B9\u008DZ\u00AA\u00B0M\x1A\t;\u00EF\u0085\x14\u00C1\r\u00EC*\u00E2Gl\"D\u00B1\x10\u00ACeW\x14\u008C6\u00CB\u00A2\x18E\u0091i&\u0093\u008Fa\u00F1\u00AB\u00B0Y$X\u00D8(\u00A9D\x11\u0089\"\x06\x02\u00EA\u00FE\u00DF\u00BCw\u008F\u00CD(:\x04dOw\x0F\u00E7w\u00B9\u00C5\u00B9\u00C4\x1A\u00AA\u00D7\u00EB?T*\u0095\u00DFINt\u00AC\u008B!\u0084\u00E9\u00D1\u00D1\u00D1\u008F\u00DDY~=\u00B4Z\u00ADMEQ\x1C\u00904\x01`sW\u00F6\u00AD\u00A4\u00CB$\u00A7\u00B2,{\u00F5\u00CD\u0082\u00F9\u00F9\u00F9\u00AD\u00EE~L\u00D2A\x00\u00EB\u00D6\u00BA\u00EA+\x05Iuw?322\u00F2\u0094\u00CDf\u00F3:\u00801\x00\u00F6\x1D\u00B0[\x0E\u00E0\u00A6\x01\b\x00\u00F4?at\u0098\u009C\x00\u00D0h4\u00B6\u0090\x1C'9\x0E\u00A0\u00F7;\u00E0{\x007\x00\u009C\u00CF\u00B2l)\u00FE\u00EC\u0092\u008CI^\x02\u00F0A\u00D2Q\x00?v\u0081o\x00L\u0093,%\u00F5\u00B8{\x00\x006\u009B\u00CD\u009F\x01\\ \u00F9\u00A7\u00BBo y\x04\u00C0\u00BF\u0092\u009E\u0091\u00FC\u00B5\x03\u00DF\x02\u00B0\x05\u00C0NI\x7F\u0099\u00D9;I'%M\u0098\u00A4 )\u0096\u00D4\u009B\u00E7\u00F9\u0095\x10\u00C2\x0E\u0092\u00B7I\u00D6H\u00BE$\u00F9\x12\u00C0/$o\u0087\x10v\u00E4y~ER\u00AF\u00A4\u00D8\u00CC\u00FE\u00B3\u00E1\u00E1\u00E1\u0087\u00EE>Hr\u00A0Z\u00AD>J\u0092\u00E4t\x1C\u00C7\x0F\u00B2,\u00DB-iR\u00D2d\u0096e\u00BB\u00E38~\u0090$\u00C9\u00E9j\u00B5\u00FA\u0088\u00E4\u0080\u00BB\x0F\u00A6i\u00BAh\x00P\x14\u00C5\x07\x00\u00AF%\u00FD!\u00E9y\u00BB\u00DD\u00BE333s\u00AD,Ks\u00F7bvv\u00F6B\u009E\u00E7\u00F7\u00CB\u00B2\\u\u00F7\u00B3\x00^w\x18\u00B0\u00D1hl7\u00B3\x1B\u0092n\u0092\u00DC\x00`\u008F\u0099]\x05\u00F0\u00CA\u00DD\u00F7\x01\u0080\u0099M\x03\u00D8\u00EC\u00EE\u00FB\x01\u00FC#\u00E9\x1D\u00C913\x1B33\u00DB\x04`5\u008A\u00A2\u00C54MO\u0084\x10v\u00B9;\u00DC\u00FD\x14\u0080%\x00K\u00EE~\u00CA\u00DD\x11B\u00D8\u0095\u00A6\u00E9\u0089(\u008A\x16\x01\u00AC\x02\u00D8H\x00\u0098\u009B\u009B\u00DBV\u0096\u00E5q3\u00FB\u00C9\u00DD\u00A7\u00F2<\u00BF\u00B5\u00B2\u00B2\u00A2\u00FE\u00FE\u00FEA\x00X^^\u00BE\u00D7\u00D7\u00D7\u00C7J\u00A5\u00B2\u00D7\u00CC&\u00DC\u00FDI\x14E\u00E7j\u00B5\u00DA\u00E3o\u009Eiaa\u00A1\u00BF(\u008Aq\u0092\u00A3\u0092\u00EA!\u0084\u00A9$I\u00DA\x00\x0E\x008L\u00F2.\u00C9s\u00B5Z\u00ED\u00C5\u0097\u00FE\u00ACU\u00B5V\u00AB\u00B5\u00BE\u00DDn\x1F\x02\u00F0[\u00C7\u00BA\u00DE\u00D3\u00D3\u00F3\u00F7\u00D0\u00D0\u00D0jw\u00F6\x13\u00A2\u00E20{\u00A0gi\u009A\x00\x00\x00\x00IEND\u00AEB`\u0082", "w16_selected_layers.png", "w16" );
w16_selected_layers;

DuScriptUI.Icon.AE_LAYERS = w12_layers;
DuScriptUI.Icon.AE_COMP = w12_comp;// ==================== |------------------| ====================
// ==================== | dulist_extension | ====================
// ==================== |------------------| ====================

/**
	* Checks if the param is an AE collection or an Array
	* @param {Array|Collection} collection - The list to check
	* @static
	* @return {boolean} true if collection is a collection, false if it's an array
*/
DuList.isAECollection = function (collection)
{
	return collection instanceof ItemCollection || collection instanceof LayerCollection || collection instanceof OMCollection || collection instanceof RQItemCollection;
}
// ==================== |----------------------| ====================
// ==================== | duscriptui_extension | ====================
// ==================== |----------------------| ====================

DuScriptUI.String.ACTIVE_COMPOSITION = "Active composition";
DuScriptUI.String.ADJUSTMENT_LAYER = "Adjustment layer";
DuScriptUI.String.ALL_COMPOSITIONS = "All Compositions";
DuScriptUI.String.BEST_QUALITY = "Use best quality";
DuScriptUI.String.CLIP = "Clip";
DuScriptUI.String.COMPOSITION_SHORT = "Comp";
DuScriptUI.String.COMPOSITION = "Composition";
DuScriptUI.String.CONTINUOUS_RASTERIZATION = "Collapse transformation / Continuous rasterization";
DuScriptUI.String.CREATE_NULL_TIP = "Create a null object.";
DuScriptUI.String.CREATE_ADJUSTMENT_TIP = "Create an adjustment layer."
DuScriptUI.String.CREATE_NULL_TIP = "Create a null object.";
DuScriptUI.String.CREATE_SHAPE_TIP = "Create a shape layer."
DuScriptUI.String.CREATE_SOLID = "Create a solid layer."
DuScriptUI.String.CURRENT_TIME = "Current time";
DuScriptUI.String.DRAFT_MODE = "Use draft mode"
DuScriptUI.String.EDIT_MODE = "Edit mode";
DuScriptUI.String.GROUP_NAME = "Group name";
DuScriptUI.String.GUIDE_LAYERS = "Guide layers";
DuScriptUI.String.IN_TIME = "In";
DuScriptUI.String.INVALID_PSEUDO_EFFECT = "Invalid pseudo effect file or match name.";
DuScriptUI.String.LAYERS = "Layers";
DuScriptUI.String.LOCK_LAYERS = "Lock layers";
DuScriptUI.String.LOCK_PROPERTIES = "Lock properties";
DuScriptUI.String.MAIN_NAME = "Main name";
DuScriptUI.String.MISSING_PSEUDO_EFFECT = "The pseudo effect file does not exist.";
DuScriptUI.String.NEW_VERSION_TITLE = "New version available";
DuScriptUI.String.NULL_LAYER = "Null";
DuScriptUI.String.OPEN_PREFS = "Open preferences";
DuScriptUI.String.OUT_TIME = "Out";
DuScriptUI.String.PROJECT = "Project";
DuScriptUI.String.PROPERTY = "Property";
DuScriptUI.String.RANGE = "Range";
DuScriptUI.String.RENDER = "Render";
DuScriptUI.String.SECONDARY_NAME = "Secondary name";
DuScriptUI.String.SELECTED_COMPOSITIONS = "Selected compositions";
DuScriptUI.String.SELECTED_PROPERTIES = "Selected properties";
DuScriptUI.String.SET_FRAME_BLENDING = "Set frame blending mode";
DuScriptUI.String.SHAPE_LAYER = "Shape";
DuScriptUI.String.SHOW_HIDE_LAYERS_TIP = "Show/Hide layers";
DuScriptUI.String.SHY_LAYERS = "Shy layers";
DuScriptUI.String.SOLID_LAYER = "Solid";
DuScriptUI.String.SOLO_LAYERS = "Toggle solo mode";
DuScriptUI.String.STAR = "Star";
DuScriptUI.String.TIME = "Time";
DuScriptUI.String.TOGGLE_3D_LAYER = "Toggle 3D layer mode";
DuScriptUI.String.TOGGLE_ADJUSTMENT_LAYER = "Toggle adjustment layer mode";
DuScriptUI.String.TOGGLE_AUDIO = "Toggle audio";
DuScriptUI.String.TOGGLE_EFFECTS = "Toggle effects";
DuScriptUI.String.TOGGLE_MOTION_BLUR = "Toggle motion blur";
DuScriptUI.String.LOCATOR = "Locator";

/**
 * Runs the installation wizard for the script (asks for files and network access, asks the language...).
 * @param {function} callback - The function to execute when ready.<br />
 * This function should be the one which loads the script.
 * @param {Panel|Window} [ui]	- A container to display the UI. A modal Dialog is created if omitted
 * @param {string} [scriptName=DuESF.scriptName] - The name of the script, used in the UI.
 * @param {boolean} [reInit=false] - Set to true to display to prompt to reinit/reinstall the script below the button to ask for file access.
 * @param {function} [reInitMethod] - A function to run in order to reinit/reinstall the script as soon as we get file access, before running the callback
 */
DuScriptUI.setupScript = function( callback, ui, scriptName, reInit, reInitMethod )
{
    function version( ) { DuScriptUI.checkUpdate( callback, ui ); }

    function language( ) { DuScriptUI.askLanguage( version, ui ); }
}

/**
 * @class
 * @name DuAELayerSelector
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A drop down selector.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.layerSelector} to create a Selector.<br />
 * The Selector inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {int} [index=0]  - The current layer index, 0 if None
 * @property {CompItem|null} [comp=null] - The composition linked to the selector.<br />
 * If set to null, the seletor will use the current active composition.
 * @property {boolean} [selectedOnly=false] - True to list only selected layers in the composition
 * @property {Selector~onChange} onChange  - The function to execute when the index changes.<br />
 * You can set your own function here, which must take no argument.<br />
 * The method is called after the index has changed.
 * @category DuScriptUI
 */

/**
 * The function to execute when the index changes.<br />
 * The method is called after the index has changed.
 * @callback DuAELayerSelector~onChange
 * @memberof DuAELayerSelector
 */

/**
 * Changes the selection and the current layer index of the selector
 * @method
 * @memberof DuAELayerSelector
 * @name setCurrentIndex
 * @param {int} index - The new layer index
 */

/**
 * Force the refresh of the layer list
 * @method
 * @memberof DuAELayerSelector
 * @name refresh
 */

/**
 * Creates a drop down selector for layers
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the selector.
 * @param {string} [helpTip=""] - The help tip.
 * @return {DuAELayerSelector} - The selector
 */
DuScriptUI.layerSelector = function( container, helpTip )
{
    helpTip = def(helpTip, '');

    //create main group
    var selector = container.add( 'group' );
    selector.orientation = 'row';
    selector.spacing = 0;
    selector.alignment = [ 'fill', 'top' ];
    selector.alignChildren = [ 'center', 'center' ];

    selector.mainGroup = selector.add( 'group' );
    selector.mainGroup.orientation = 'row';
    selector.mainGroup.margins = 1;
    selector.mainGroup.alignment = [ 'fill', 'fill' ];
    selector.mainGroup.alignChildren = [ 'center', 'center' ];
    selector.helpTip = helpTip;
    DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.TRANSPARENT );

    //init
    selector.index = 0;
    selector.comp = null;
    selector.selectedOnly = false;
    selector.pauseRefresh = false;

    selector.fillerL = selector.mainGroup.add( 'statictext', undefined, " " );
    selector.fillerL.alignment = [ 'left', 'fill' ];
    selector.fillerL.size = [5, -1];

    //the menu button
    selector.menuButton = selector.mainGroup.add( 'image', undefined, w12_layers.binAsString );
    selector.menuButton.alignment = [ 'left', 'center' ];
    selector.menuButton.helpTip = helpTip;

    //the text
    selector.label = selector.mainGroup.add( 'statictext', undefined, '' );
    selector.label.helpTip = '';
    selector.label.alignment = [ 'fill', 'center' ];
    selector.label.helpTip = helpTip;
    DuScriptUI.setTextColor( selector.label, DuColor.Color.APP_TEXT_COLOR );

    //create popup
    selector.popup = new Window( 'palette', '', undefined,
    {
        borderless: true
    } );
    selector.popup.margins = 2;
    selector.popup.spacing = 0;
    selector.popup.closeButton = DuScriptUI.button( selector.popup, i18n._("Cancel") );
    selector.popup.closeButton.onClick = selector.popup.onDeactivate = function()
    {
        selector.popup.hide();
    };
    selector.popup.buttons = selector.popup.add( 'group' );
    selector.popup.buttons.orientation = 'column';
    selector.popup.buttons.margins = 0;
    selector.popup.buttons.spacing = 0;
    selector.popup.hide();

    selector.popup.list = selector.popup.add( 'listbox', undefined );

    selector.clicked = function( e )
    {
        selector.refresh();

        if (e.ctrlKey) {
            selector.setCurrentIndex(0);
            return;
        }

        selector.popup.list.minimumSize.width = selector.popup.list.maximumSize.width = selector.size.width;
        selector.popup.list.minimumSize.height = selector.popup.list.maximumSize.height = selector.size.height * 10;

        selector.popup.layout.layout();
        selector.popup.layout.resize();

        var x = e.screenX - e.clientX;
        var y = e.screenY - e.clientY;
        selector.popup.location = [ x, y ];

        selector.popup.show();
    }

    //the pick button
    selector.pickGroup = selector.add('group');
    selector.pickGroup.margins = 0;
    selector.pickGroup.alignment = ['right', 'fill'];
    selector.pickButton = selector.pickGroup.add( 'image', undefined, w12_eye_dropper.binAsString );
    selector.pickButton.alignment = [ 'center', 'center' ];

    selector.popup.list.add( 'item', i18n._p( "Select", "None" ) );
    selector.popup.list.selection = 0;

    selector.refresh = function()
    {
        var comp = selector.comp;
        if ( !comp ) comp = DuAEProject.getActiveComp();
        if ( !comp ) return;

        selector.pauseRefresh = true;

        var layers;
        if ( selector.selectedOnly )
        {
            layers = DuAELayer.sortByIndex ( comp.selectedLayers );
        }
        else layers = comp.layers;

        var prevIndex = selector.index;
        selector.popup.list.removeAll();
        selector.popup.list.add( 'item', i18n._p( "Select", "None" ) );
        var it = new DuList( layers );
        it.do( function( layer )
        {
            if (layer) selector.popup.list.add( 'item', layer.index + ' | ' + layer.name );
        } );
        selector.setCurrentIndex( prevIndex, true );

        selector.pauseRefresh = false;
    };

    selector.setCurrentIndex = function( index, quiet )
    {
        quiet = def(quiet, false);

        if ( index < 0 ) return;

        selector.pauseRefresh = true;

        var found = false;

        if ( index > 0 )
        {
            for ( var i = 0, num = selector.popup.list.items.length; i < num; i++ )
            {
                var t = selector.popup.list.items[ i ].text;
                var info = t.split( ' | ' );
                var idx = parseInt( info[ 0 ] );
                if ( index == idx )
                {
                    selector.label.text = t;
                    selector.popup.list.selection = i;
                    found = true;
                    break;
                }
            }
        }

        if (!found)
        {
            selector.popup.list.selection = 0;
            selector.label.text = i18n._p( "Select", "None" );
        }

        selector.index = index;
        selector.pauseRefresh = false;

        if (!quiet) selector.onChange();
    };

    selector.popup.list.onChange = function()
    {
        if ( selector.pauseRefresh ) return;
        var t = selector.popup.list.selection.text;
        var index = parseInt( t.split( ' | ' )[ 0 ] );
        selector.setCurrentIndex( index );
        selector.popup.hide();
    };

    //mouse over
    selector.highlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();
        DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
        DuScriptUI.highlightedControls.push( selector );
    }

    selector.pickHighlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();
        DuScriptUI.setBackgroundColor( selector.pickGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
        DuScriptUI.highlightedControls.push( selector );
    }

    selector.dim = function( e )
    {
        DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.TRANSPARENT );
        DuScriptUI.setBackgroundColor( selector.pickGroup, DuColor.Color.TRANSPARENT );
    }

    selector.pick = function()
    {
        var layers = DuAEComp.getSelectedLayers();
        if ( layers.length > 0 ) selector.setCurrentIndex( layers[ 0 ].index );
    }

    selector.getLayer = function()
    {
        if ( selector.index < 1 ) return null;
        var comp = selector.comp;
        if ( !comp ) comp = DuAEProject.getActiveComp();
        if ( !comp ) return null;
        try
        {
            return comp.layer( selector.index )
        }
        catch ( e )
        {
            DuDebug.throwError("Layer selection error.", "DuAELayerSelector.getLayer", e);
        };
    }

    selector.mainGroup.addEventListener( "mouseover", selector.highlight );
    selector.pickButton.addEventListener( "mouseover", selector.pickHighlight );
    selector.pickGroup.addEventListener( "mouseover", selector.pickHighlight );

    selector.pickButton.addEventListener( "click", selector.pick );
    selector.mainGroup.addEventListener( "click", selector.clicked, true );
    
    selector.onChange = function() {};

    selector.refresh();

    selector.setCurrentIndex(0);

    return selector;
}

/**
 * @class
 * @name DuAECompSelector
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A drop down selector.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.compSelector} to create a Selector.<br />
 * The Selector inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {int} [id=0]  - The current compitem id, 0 if None
 * @property {CompItem[]} [comps] - The compositions listed in the selector.
 * @property {Selector~onChange} onChange  - The function to execute when the index changes.<br />
 * You can set your own function here, which must take no argument.<br />
 * The method is called after the index has changed.
 * @property {Selector~filterComps} filterComps - A function which gets the comps to set in the selector.<br />
 * The default function will get all the comps in the project,<br />
 * You can set your own function here, which must take no argument.<br />
 * The function must return an array of CompItem.
 * @category DuScriptUI
 */

/**
 * The function to execute when the index changes.<br />
 * The method is called after the index has changed.
 * @callback DuAECompSelector~onChange
 * @memberof DuAECompSelector
 */


/**
 * A function which gets the comps to set in the selector.<br />
 * The default function will get all the comps in the project,<br />
 * assign another function to this callback if you need to filter these comps.<br />
 * The function must return an array of CompItem.
 * @callback DuAECompSelector~filterComps
 * @memberof DuAECompSelector
 * @return {CompItem[]} The compositions to set in the selector.
 */

/**
 * Changes the selection and the current comp id of the selector
 * @method
 * @memberof DuAECompSelector
 * @name setCurrentId
 * @param {int} index - The new comp id
 */

/**
 * Refreshes the comp list
 * @method
 * @memberof DuAECompSelector
 * @name refresh
 * @param {CompItem[]} [comps] - The list of compositions. By default, will use {@link DuAECompSelector.filterComps()} to get the comps.
 */

/**
 * Gets the selected comp
 * @method
 * @memberof DuAECompSelector
 * @name getComp
 * @return {CompItem|null} the selected comp
 */

/**
 * Creates a drop down selector for compositions
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the selector.
 * @param {string} [helpTip=""] - The help tip.
 * @return {DuAECompSelector} - The selector
 */
DuScriptUI.compSelector = function( container, helpTip )
{
    helpTip = def(helpTip, '');

    //create main group
    var selector = container.add( 'group' );
    selector.orientation = 'row';
    selector.spacing = 0;
    selector.alignment = [ 'fill', 'top' ];
    selector.alignChildren = [ 'center', 'center' ];

    selector.mainGroup = selector.add( 'group' );
    selector.mainGroup.orientation = 'row';
    selector.mainGroup.margins = 1;
    selector.mainGroup.alignment = [ 'fill', 'fill' ];
    selector.mainGroup.alignChildren = [ 'center', 'center' ];
    selector.helpTip = helpTip;
    DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.TRANSPARENT );

    //init
    selector.id = 0;
    selector.comp = null;
    selector.selectedOnly = false;
    selector.pauseRefresh = false;

    selector.fillerL = selector.mainGroup.add( 'statictext', undefined, " " );
    selector.fillerL.alignment = [ 'left', 'fill' ];
    selector.fillerL.size = [5, -1];

    //the menu button
    selector.menuButton = selector.mainGroup.add( 'image', undefined, w12_comp.binAsString );
    selector.menuButton.alignment = [ 'left', 'center' ];
    selector.menuButton.helpTip = helpTip;

    //the text
    selector.label = selector.mainGroup.add( 'statictext', undefined, '' );
    selector.label.helpTip = '';
    selector.label.alignment = [ 'fill', 'center' ];
    selector.label.helpTip = helpTip;
    DuScriptUI.setTextColor( selector.label, DuColor.Color.APP_TEXT_COLOR );

    //create popup
    selector.popup = new Window( 'palette', '', undefined,
    {
        borderless: true
    } );
    selector.popup.margins = 2;
    selector.popup.spacing = 0;
    selector.popup.closeButton = DuScriptUI.button( selector.popup, i18n._( "Cancel" ) );
    selector.popup.closeButton.onClick = selector.popup.onDeactivate = function()
    {
        selector.popup.hide();
    };
    selector.popup.buttons = selector.popup.add( 'group' );
    selector.popup.buttons.orientation = 'column';
    selector.popup.buttons.margins = 0;
    selector.popup.buttons.spacing = 0;
    selector.popup.hide();

    selector.popup.list = selector.popup.add( 'listbox', undefined );

    selector.clicked = function( e )
    {
        selector.refresh();

        if (e.ctrlKey) {
            selector.setCurrentId(0);
            return;
        }

        selector.popup.list.minimumSize.width = selector.popup.list.maximumSize.width = selector.size.width;
        selector.popup.list.minimumSize.height = selector.popup.list.maximumSize.height = selector.size.height * 10;

        selector.popup.layout.layout();
        selector.popup.layout.resize();

        var x = e.screenX - e.clientX;
        var y = e.screenY - e.clientY;
        selector.popup.location = [ x, y ];

        selector.popup.show();
    }

    //the pick button
    selector.pickGroup = selector.add('group');
    selector.pickGroup.margins = 0;
    selector.pickGroup.alignment = ['right', 'fill'];
    selector.pickButton = selector.pickGroup.add( 'image', undefined, w12_eye_dropper.binAsString );
    selector.pickButton.alignment = [ 'center', 'center' ];

    var itemNone = selector.popup.list.add( 'item', i18n._p( "Select", "None" ) );
    itemNone.id = 0;
    selector.popup.list.selection = 0;

    selector.refresh = function()
    {
        var comps = selector.filterComps();
        selector.pauseRefresh = true;

        var prevId = selector.id;
        selector.popup.list.removeAll();
        var itemNone = selector.popup.list.add( 'item', i18n._p( "Select", "None" ) );
        itemNone.id = 0;
        var it = new DuList( comps );
        it.do( function( comp )
        {
            var item = selector.popup.list.add( 'item', comp.name );
            item.id = comp.id;
        } );
        selector.setCurrentId( prevId, true );

        selector.pauseRefresh = false;
    };

    selector.getComp = function()
    {
        if ( selector.id == 0 ) return null;
        return DuAEProject.getItemById( selector.id );
    }

    selector.setCurrentId = function( id, quiet )
    {
        quiet = def(quiet, false);

        if ( id < 0 ) id = 0;

        selector.pauseRefresh = true;

        var found = false;

        if (id > 0)
        {
            for ( var i = 0, num = selector.popup.list.items.length; i < num; i++ )
            {
                var item = selector.popup.list.items[ i ];
                if ( id == item.id )
                {
                    selector.popup.list.selection = i;
                    selector.label.text = selector.popup.list.selection.text;
                    found = true;
                    break;
                }
            }
        }

        if (!found)
        {
            selector.popup.list.selection = 0;
            selector.label.text = i18n._p( "Select", "None" );
        }

        selector.id = id;

        selector.pauseRefresh = false;

        if (!quiet) selector.onChange();
    };

    selector.popup.list.onChange = function()
    {
        if ( selector.pauseRefresh ) return;
        selector.setCurrentId( selector.popup.list.selection.id );
        selector.popup.hide();
    };

    //mouse over
    selector.highlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();
        DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
        DuScriptUI.highlightedControls.push( selector );
    }

    selector.pickHighlight = function( e )
    {
        e.stopPropagation();
        DuScriptUI.dimControls();
        DuScriptUI.setBackgroundColor( selector.pickGroup, DuColor.Color.APP_HIGHLIGHT_COLOR.darker() );
        DuScriptUI.highlightedControls.push( selector );
    }

    selector.dim = function( e )
    {
        DuScriptUI.setBackgroundColor( selector.mainGroup, DuColor.Color.TRANSPARENT );
        DuScriptUI.setBackgroundColor( selector.pickGroup, DuColor.Color.TRANSPARENT );
    }

    selector.pick = function()
    {
        var comp = DuAEProject.getActiveComp();
        if ( comp ) selector.setCurrentId( comp.id );
    }

    selector.mainGroup.addEventListener( "mouseover", selector.highlight );
    selector.pickButton.addEventListener( "mouseover", selector.pickHighlight );
    selector.pickGroup.addEventListener( "mouseover", selector.pickHighlight );

    selector.mainGroup.addEventListener( "click", selector.clicked, true );
    selector.pickButton.addEventListener( "click", selector.pick );

    selector.onChange = function() {};

    selector.filterComps = function()
    {
        var comps = DuAEProject.getComps();
        return comps;
    };

    selector.refresh();

    selector.setCurrentId(0);

    return selector;
}

/**
 * @class
 * @name DuAELayerPicker
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A picker for layers with labels.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.layerPicker} to create a picker.<br />
 * The picker inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {DuAELayerSelector[]} selectors  - The layer selectors
 * @property {string[]} inputs - The labels texts
 * @category DuScriptUI
 */

/**
 * Empties the DuAELayerPicker
 * @method
 * @memberof DuAELayerPicker
 * @name removeAll
 */

/**
 * Adds a new line on the DuAELayerPicker
 * @method
 * @memberof DuAELayerPicker
 * @name addSelector
 * @param {string} name The display name of the selector.
 * @return {DuAELayerSelector} The added DuAELayerSelector
 */

/**
 * Creates a two-column group to allow the user to pick layers
 * @param {Window|Panel|Group} container - The ScriptUI Object which will contain and display the selector.
 * @return {DuAELayerPicker} The picker.
 */
DuScriptUI.layerPicker = function( container )
{
    var name = i18n._( "Select layers" );

    var layerPicker = container.add( 'group' );
    layerPicker.orientation = 'row';
    layerPicker.margins = 0;
    layerPicker.spacing = 2;
    layerPicker.alignment = [ 'fill', 'top' ];

    layerPicker.labelsGroup = layerPicker.add( 'group' );
    layerPicker.labelsGroup.orientation = 'column';
    layerPicker.labelsGroup.margins = 0;
    layerPicker.labelsGroup.spacing = 2;
    layerPicker.labelsGroup.alignment = [ 'left', 'fill' ];
    layerPicker.labelsGroup.alignChildren = [ 'left', 'fill' ];

    layerPicker.selectorsGroup = layerPicker.add( 'group' );
    layerPicker.selectorsGroup.orientation = 'column';
    layerPicker.selectorsGroup.margins = 0;
    layerPicker.selectorsGroup.spacing = 2;
    layerPicker.selectorsGroup.alignment = [ 'fill', 'fill' ];

    layerPicker.selectors = [];
    layerPicker.inputs = [];

    layerPicker.removeAll = function()
    {
        layerPicker.selectors = [];
        layerPicker.inputs = [];
        for ( var i = layerPicker.labelsGroup.children.length - 1; i >= 0; i-- )
        {
            layerPicker.labelsGroup.remove( layerPicker.labelsGroup.children[ i ] );
            layerPicker.selectorsGroup.remove( layerPicker.selectorsGroup.children[ i ] );
        }
    }

    layerPicker.addSelector = function( inputLabel )
    {
        var l = layerPicker.labelsGroup.add( 'statictext', undefined, inputLabel );

        var sel = DuScriptUI.layerSelector( layerPicker.selectorsGroup );
        layerPicker.selectors.push( sel );
        sel.alignment = [ 'fill', 'fill' ];

        //l.minimumSize.height = l.maximumSize.height = 18;

        return sel;
    }

    return layerPicker;
}

/**
 * @class
 * @name DuAELayerPickerDialog
 * @classdesc For use with {@link DuScriptUI}.<br />
 * A picker for layers with labels.<br />
 * This is not a real class, and cannot be instanciated.<br />
 * Use {@link DuScriptUI.layerPickerDialog} to create a picker.<br />
 * The picker inherits the Group object from ScriptUI and has all of its properties and methods.
 * @property {DuAELayerPicker} layerPicker The layer picker inside the dialog.
 * @property {bool} accepted This property is true if the user has clicked the OK button, false otherwise.
 * @category DuScriptUI
 */

/**
 * Empties the DuAELayerPicker<br />
 * This is a convenience function equivalent to {@link DuAELayerPickerDialog.layerPicker.removeAll}.
 * @method
 * @memberof DuAELayerPickerDialog
 * @name removeAll
 */

/**
 * Adds a new line on the DuAELayerPicker
 * This is a convenience function equivalent to {@link DuAELayerPickerDialog.layerPicker.addSelector}.
 * @method
 * @memberof DuAELayerPickerDialog
 * @name addSelector
 * @return {DuAELayerSelector} The added DuAELayerSelector
 */

/**
 * Gets the layers picked by the user.
 * @method
 * @memberof DuAELayerPickerDialog
 * @name getLayers
 * @return {Layer[]} The layers picked (null if the user has set none in the selector), in the display order.
 */

/**
 * The function called when the dialog is accepted.
 * @callback DuAELayerPickerDialog~onAccept
 * @memberof DuAELayerPickerDialog
 */

/**
 * Creates a dialog with a DuAELayerPicker<br />
 * Use {@link DuScriptUI.showUI} to show it after creation.
 * @param {string} title - The title of the dialog.
 * @return {DuAELayerPickerDialog} The dialog window.
 */
DuScriptUI.layerPickerDialog = function( title )
{
    var dialog = new Window( 'palette', title, undefined,
    {
        resizeable: true
    } );
    dialog.minimumSize.width = 300;

    dialog.margins = 0;
    dialog.orientation = 'column';

    dialog.layerPicker = DuScriptUI.layerPicker( dialog );

    dialog.onAccept = function() {};
    dialog.removeAll = dialog.layerPicker.removeAll;
    dialog.addSelector = dialog.layerPicker.addSelector;
    dialog.getLayers = function()
    {
        var layers = [];
        for ( var i = 0, num = dialog.layerPicker.selectors.length; i < num; i++ )
        {
            var sel = dialog.layerPicker.selectors[ i ];
            layers.push( sel.getLayer() );
        }
        return layers;
    }

    dialog.accepted = false;

    //add buttons
    var validGroup = dialog.add( 'group' );
    validGroup.alignment = [ 'fill', 'bottom' ];
    var validGroupCancelButton = DuScriptUI.button( validGroup, "Cancel", DuScriptUI.Icon.BACK, "Cancel" );
    var validGroupValidButton = DuScriptUI.button( validGroup, "OK", DuScriptUI.Icon.CHECK, "OK" );

    validGroupValidButton.onClick = function()
    {
        dialog.accepted = true;
        dialog.hide();
        dialog.onAccept();
    };
    validGroupCancelButton.onClick = function()
    {
        dialog.hide();
    };

    return dialog;
}

DuScriptUI.refreshPanel = function( panel, scriptFile )
{
    var ok = DuScriptUI.refreshWindow( panel, scriptFile );
    if (ok) return true;
    if (panel instanceof Panel && scriptFile.exists )
    {
        var scriptName = DuPath.getName(scriptFile);
        // Close
        DuAE.openScriptUIPanel( scriptName );
        // Open
        DuAE.openScriptUIPanel( scriptName );
        return true;
    }
    return false;
}

/**
 * Creates a selector to choose a selection mode
 * @param {DuAE.SelectionMode} [minimalMode=DuAE.SelectionMode.SELECTED_PROPERTIES] The lowest mode to use
 * @return {DuSelector} The selector
 */
DuScriptUI.selectionModeSelector = function( container, minimalMode )
{
    minimalMode = def(minimalMode, DuAE.SelectionMode.SELECTED_PROPERTIES);
    var selector = DuScriptUI.selector( container );
    if (minimalMode <= DuAE.SelectionMode.SELECTED_PROPERTIES) selector.addButton(
        DuScriptUI.String.SELECTED_PROPERTIES,
        w16_selected_props
    );
    if (minimalMode <= DuAE.SelectionMode.SELECTED_LAYERS) selector.addButton(
        DuScriptUI.String.SELECTED_LAYERS,
        w16_selected_layers
    );
    if (minimalMode <= DuAE.SelectionMode.ACTIVE_COMPOSITION) selector.addButton(
        DuScriptUI.String.ACTIVE_COMPOSITION,
        w16_layers
    );
    if (minimalMode <= DuAE.SelectionMode.SELECTED_COMPOSITIONS) selector.addButton(
        DuScriptUI.String.SELECTED_COMPOSITIONS,
        w16_selected_compositions
    );
    if (minimalMode <= DuAE.SelectionMode.ALL_COMPOSITIONS) selector.addButton(
        DuScriptUI.String.ALL_COMPOSITIONS,
        w16_compositions
    );

    return selector;
}// ==================== |------| ====================
// ==================== | core | ====================
// ==================== |------| ====================

/* License
	DuAEF - Duduf After Effects Framework

	Copyright (c) 2008 - 2021 Nicolas Dufresne, RxLaboratory

	https://RxLaboratory.org

	This file is part of DuAEF.

		DuAEF is free software: you can redistribute it and/or modify
		it under the terms of the GNU General Public License as published by
		the Free Software Foundation, either version 3 of the License, or
		(at your option) any later version.

		DuAEF is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.

		You should have received a copy of the GNU General Public License
		along with DuAEF. If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * The Duduf After Effects ExtendScript Framework.<br />
 * by {@link https://RxLaboratory.org RxLaboratory} and {@link http://duduf.com Duduf}.
 * @example
 * // Encapsulate everything to avoid global variables!
 * // The parameter is either undefined (stand alone script) or the panel containing the ui (ScriptUI)
 * (function(thisObj)
 * {
 *      // Include the framework
 *      #include "DuAEF.jsxinc";
 *      
 *      // Running the init() method of DuAEF is required to setup everything properly.
 *      DuAEF.init( "YourScriptName", "1.0.0", "YourCompanyName" );
 *      
 *      // These info can be used by the framework to improve UX, but they're optional
 *      DuESF.chatURL = 'http://chat.rxlab.info'; // A link to a live-chat server like Discord or Slack...
 *      DuESF.bugReportURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a bug report form
 *      DuESF.featureRequestURL = 'https://github.com/RxLaboratory/DuAEF_Dugr/issues/new/choose'; // A link to a feature request form
 *      DuESF.aboutURL = 'http://rxlaboratory.org/tools/dugr'; // A link to the webpage about your script
 *      DuESF.docURL = 'http://dugr.rxlab.guide'; // A link to the documentation of the script
 *      DuESF.scriptAbout = 'Duduf Groups: group After Effects layers!'; // A short string describing your script
 *      DuESF.companyURL = 'https://rxlaboratory.org'; // A link to your company's website
 *      DuESF.rxVersionURL = 'http://version.rxlab.io' // A link to an RxVersion server to check for updates
 *      
 *      // Build your UI here, declare your methods, etc.
 * 
 *      // This will be our main panel
 *      var ui = DuScriptUI.scriptPanel( thisObj, true, true, new File($.fileName) );
 *      ui.addCommonSettings(); // Automatically adds the language settings, location of the settings file, etc
 *
 *      DuScriptUI.staticText( ui.settingsGroup, "Hello world of settings!" ); // Adds a static text to the settings panel
 *      DuScriptUI.staticText( ui.mainGroup, "Hello worlds!" ); // Adds a static text to the main panel
 *      
 *      // When you're ready to display everything
 *      DuScriptUI.showUI(ui);
 *
 *      // Note that if you don't have a UI or if you don't use DuScriptUI to show it,
 *      // you HAVE TO run this method before running any other function:
 *      // DuAEF.enterRunTime();
 *  
 * })(this);
 * @namespace
 * @author Nicolas Dufresne and contributors
 * @copyright 2017 - 2021 Nicolas Dufresne, RxLaboratory
 * @version 1.0.0
 * @license GPL-3.0 <br />
 * DuAEF is free software: you can redistribute it and/or modify<br />
 * it under the terms of the GNU General Public License as published by<br />
 * the Free Software Foundation, either version 3 of the License, or<br />
 * (at your option) any later version.<br />
 *<br />
 * DuAEF is distributed in the hope that it will be useful,<br />
 * but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br />
 * GNU General Public License for more details.<br />
 *<br />
 * You should have received a copy of the GNU General Public License<br />
 * along with DuAEF. If not, see {@link http://www.gnu.org/licenses/}.
 * @category DuAEF
 */
var DuAEF = {};
/**
 * The Current DuAEF Version
 * @readonly
 * @memberof DuAEF
 * @type {string}
 */
DuAEF.version = new DuVersion('1.0.0');
/**
 * The current DuAEF File
 * @readonly
 * @memberof DuAEF
 * @type {File}
 */
DuAEF.file = new File( $.fileName );


//Initializes DuAEF
( function()
{
    //Update AE Dark Grey Color with the actual color
    var aeColor = app.themeColor( 78 );
	aeColor.push(1);

    DuColor.Color.AE_DARK_GREY = aeColor;
} )();

/**
 * This method has to be called once at the very beginning of the script, just after the inclusion of DuAEF <code>#include DuAEF.jsxinc</code>
 * @param {string} [scriptName="DuAEF"] - The name of your script, as it has to be displayed in the UI and the filesystem
 * @param {string} [scriptVersion="0.0.0"] - The version of your script, in the form "XX.XX.XX-Comment", for example "1.0.12-Beta". The "-Comment" part is optional.
 */
DuAEF.init = function( scriptName, scriptVersion, companyName )
{
    DuESF.init(scriptName, scriptVersion, companyName);
}

/**
 * This method has to be called once at the end of the script, when everything is ready and the main UI visible (after any prompt or setup during startup).
 */
DuAEF.enterRunTime = function()
{
    DuESF.enterRunTime();
}
// ==================== |----| ====================
// ==================== | ae | ====================
// ==================== |----| ====================

/**
	* Constructs a new KeySpatialProperty
	* @class DuAEKeySpatialProperties
	* @classdesc Spatial properties of a {@linkcode DuAEKeyframe}.
	* @param {DuAEKeySpatialProperties} [other] Another DuAEKeySpatialProperties to create a copy.
	* @property {float[]|null}	  [inTangent=null]     - The incoming spatial tangent
	* @property {float[]|null}    [outTangent=null]            -  The outgoing spatial tangent
	* @property {boolean}      [_continuous=true]         - true if the specified keyframe has spatial continuity
	* @property {boolean}	[_autoBezier=false]			- true if the specified keyframe has temporal auto-Bezier interpolation
	* @property {boolean}	[_roving=false]	- true if the specified keyframe is roving
	* @category DuAEF
*/
function DuAEKeySpatialProperties ( other ){
	this.inTangent = null;
	this.outTangent = null;
	this._continuous = true;
	this._autoBezier = false;
	this._roving = false;
	if (isdef( other ) && other instanceof DuAEKeySpatialProperties) {
		this.inTangent = other.inTangent;
		this.outTangent = other.outTangent;
		this._continuous = other._continuous;
		this._autoBezier = other._autoBezier;
		this._roving = other._roving;
	}
}

/**
 * Clones the DuAEKeySpatialProperties and returns the new one.
 * @return {DuAEKeySpatialProperties} The new DuAEKeySpatialProperties.
 */
DuAEKeySpatialProperties.prototype._clone = function() {
	return new DuAEKeySpatialProperties( this );
}

/**
	 Constructs a new DuAEKeyframe
	* @class DuAEKeyframe
	* @classdesc Properties of an After Effects Keyframe, as returned by {@linkcode DuAEProperty.keys} or {@linkcode DuAEProperty.keyAtIndex} or {@linkcode DuAEProperty.keyAtTime}.
	* @param {DuAEKeyframe} [other] Another keyframe to create a copy.
	* @property {float}	  _time     - The keyframe time
	* @property {null|folat[]|float|MarkerValue|int|Shape|TextDocument}    value  -  The keyframe value
	* @property {KeyframeInterpolationType}   _inInterpolationType  - The incoming temporal interpolation type
	* @property {KeyframeInterpolationType}	_outInterpolationType		- The outgoing temporal interpolation type
	* @property {boolean}	_spatial	- true if this keyframe has a spatial value
	* @property {DuAEKeySpatialProperties}	spatialProperties	- the spatial properties {@linkcode DuAEKeySpatialProperties} of this keyframe
	* @property {KeyframeEase[]}	inEase	- The incoming temporal ease. The number of objects in the Array depends on the value type
	* @property {KeyframeEase[]}	outEase	- The outgoing temporal ease. The number of objects in the Array depends on the value type
	* @property {boolean}	_continuous	- true if the keyframe has temporal continuity
	* @property {boolean}	_autoBezier	- true if the keyframe has temporal auto-Bezier interpolation
	* @property {int}		_index	- The index of the keyFrame. Warning: not updated when another key frame is added on the property some time before this key._time!
	* @category DuAEF
*/
function DuAEKeyframe( other ){
	this._time = 0;
	this.value = null;
	this._inInterpolationType = KeyframeInterpolationType.LINEAR;
	this._outInterpolationType = KeyframeInterpolationType.LINEAR;
	this._spatial = false;
	this.spatialProperties = new DuAEKeySpatialProperties();
	this.inEase = null;
	this.outEase = null;
	this._continuous = false;
	this._autoBezier = false;
	this._index = 0;
	this.label = 0;
	if (isdef( other ) && other instanceof DuAEKeyframe) {
		this._time = other._time;
		this.value = other.value;
		this._inInterpolationType = other._inInterpolationType;
		this._outInterpolationType = other._outInterpolationType;
		this._spatial = other._spatial;
		this.spatialProperties = other.spatialProperties._clone();
		this.inEase = other.inEase;
		this.outEase = other.outEase;
		this._continuous = other._continuous;
		this._autoBezier = other._autoBezier;
		this._index = other._index;
		this.label = other.label;
	}
}

/**
 * Clones the keyframe and returns the new one. <br />
 * The keyframe is not added to any property, only the JS object is cloned. Use {@link DuAEProperty.setKey} to add it to a specific property.
 * @return {DuAEKeyframe} The new keyframe.
 */
DuAEKeyframe.prototype._clone = function() {
	return new DuAEKeyframe( this );
}

/**
	* Constructs a new animation
	* @class DuAEPropertyAnimation
	* @classdesc Describes the animation of an After Effects property
	* @property {string}	  [_name=""]     - The property name
	* @property {string}    [_matchName=""]  - The property matchName
	* @property {DuAEKeyframe[]}   [keys=[]]  - The keyframes of the animation
	* @property {null|float[]|float|MarkerValue|int|Shape|TextDocument}	[startValue=null]		- The value at the beginning of the animation
	* @property {string}	[expression=""]		- The expression on the property, if any.
	* @property {string}   [type="anim"]  - Read Only.
	* @property {float}   startTime  - Read Only. The starting time of the animation.
	* @property {float}   endTime  - Read Only. The ending time of the animation.
	* @category DuAEF
*/
function DuAEPropertyAnimation()
{
	this._name = "";
	this._matchName = "";
	this.keys = [];
	this.startValue = null;
	this.type = "anim";
	this.expression = "";
	this.dimensions = 0;
	this.startTime = null;
	this.endTime = null;
}

/**
	* Constructs a new group animation
	* @class DuAEPropertyGroupAnimation
	* @classdesc Contains all DuAEPropertyAnimation from an After Effects PropertyGroup
	* @property {string}	  [_name=""]     - The property name
	* @property {string}    [_matchName=""]  - The property matchName
	* @property {DuAEPropertyAnimation[]|DuAEPropertyGroupAnimation[]}   [anims=[]]  - The animations in the group
	* @property {string}   [type="group"]  - Read Only.
	* @property {float}   startTime  - Read Only. The starting time of the animation.
	* @property {float}   endTime  - Read Only. The ending time of the animation.
	* @category DuAEF
*/
function DuAEPropertyGroupAnimation()
{
	this._name = "";
	this._matchName = "";
	this.anims = [];
	this.type = "group";
	this.startTime = null;
	this.endTime = null;
}

/**
	* Constructs a new layer animation
	* @class DuAELayerAnimation
	* @classdesc Contains all DuAEPropertyGroupAnimation from an After Effects Layer
	* @property {string}	  [_name=""]     - The property name
	* @property {int}    [_index=""]  - The index of the layer
	* @property {DuAEPropertyGroupAnimation[]} [anims=[]] - All the animations of the layer
	* @property {float} [firstKeyFrameTime=0] - The time of the first keyframe
	* @property {string}   [type="layer"]  - Read Only.
	* @property {float}   startTime  - Read Only. The starting time of the animation.
	* @property {float}   endTime  - Read Only. The ending time of the animation.
	* @category DuAEF
*/
function DuAELayerAnimation()
{
	this._name = "";
	this._index = 0;
	this.anims = [];
	this.type = 'layer';
	this.startTime = null;
	this.endTime = null;
}

/**
	* Constructs a new DuAEPropertyExpression
	* @class DuAEPropertyExpression
	* @classdesc This class describes an expression and the property containing it.<br />
	* It is used by the cache engine of DuAEF to work on expressions without applying them and improve performance.<br />
	* This class is very similar to {@link DuAEProperty} except it is lighter and faster, to improve the performance of the expression cache.
	* @param {PropertyBase|DuAEPropertyExpression|DuAEProperty} property - The property. If a DuAEPropertyExpression is provided, the constructor returns it (it does not make a copy).<br />
	* This makes it easy to avoid type checking, as you can always pass any property or DuAEPropertyExpression to the constructor to be sure to handle a DuAEPropertyExpression, without any impact on performance.
	* @category DuAEF
*/
function DuAEPropertyExpression(property)
{
	var propInfo;
	if (property instanceof DuAEPropertyExpression)
	{
		property.expression = property.getProperty().expression;
		return property;
	}
	else if (property instanceof DuAEProperty)
	{
		propInfo = property;
		property = propInfo.getProperty();

	}
	else 
	{
		propInfo = new DuAEProperty( property );
	}

	/**
		* The original property containing the expression
		* @memberof DuAEPropertyExpression.prototype
		* @name property
		* @type {PropertyBase}
		*/
	this.property = property;
	/**
		* The original layer containing the expression
		* @memberof DuAEPropertyExpression.prototype
		* @name layer
		* @type {LayerItem}
		*/
	this.layer = propInfo.layer;
	/**
		* The original composition containing the expression
		* @memberof DuAEPropertyExpression.prototype
		* @name comp
		* @type {CompItem}
		*/
	this.comp = propInfo.comp;
	/**
		* true if there's no expression in this property
		* @memberof DuAEPropertyExpression.prototype
		* @name empty
		* @type {Boolean}
		*/
	this.empty = false;

	if ( !propInfo.riggable() ) {
		this.empty = true;
		return;
	} 
	if (property.expression == '') {
		this.empty = true;
		return;
	}
	
	/**
		* The Array containing the indices in all parent property groups containing this property.<br />
		* This is used to retrieve the property in case the object becomes invalid.
		* @memberof DuAEPropertyExpression.prototype
		* @name parentIndices
		* @type {int[]}
		*/
	this.parentIndices  = [property.propertyIndex];
	/**
		* true if the property belongs to an effect.
		* @memberof DuAEPropertyExpression.prototype
		* @name isEffect
		* @type {Boolean}
		*/
	this.isEffect = property.isEffect;
	if (!this.isEffect)
	{
		var parentProp = property;
		while (parentProp.parentProperty)
		{
			// Traverse up the property tree
			parentProp = parentProp.parentProperty;
			this.isEffect = parentProp.isEffect;
			this.parentIndices.unshift(parentProp.propertyIndex);
			if (this.isEffect) break;
		}
	}

	/**
		* The expression in the property
		* @memberof DuAEPropertyExpression.prototype
		* @name expression
		* @type {string}
		*/
	this.expression = property.expression;

	/**
		* true if the expression has been changed in the cache and needs to be re-applied to the property.
		* @memberof DuAEPropertyExpression.prototype
		* @name changed
		* @type {Boolean}
		*/
	this.changed = false;
}

/**
 * Returns the original property, fixing it if the object has become invalid.
 * @return {PropertyBase} The property.
 */
DuAEPropertyExpression.prototype.getProperty = function()
{
	if (this.isEffect)
	{
		var parentProp = this.layer("ADBE Effect Parade");
		for (var i = 0 ; i < this.parentIndices.length ; i++)
		{
			parentProp = parentProp(this.parentIndices[i]);
		}
		this.property = parentProp;
	}
	return this.property;
}

/**
 * Applies the expression back to the actual properties in After Effects, if and only if it's been modified.
 */
DuAEPropertyExpression.prototype.apply = function()
{
	if (this.layer.locked) return;
	if (!this.changed) return;

	var prop = this.getProperty();
	prop.expression = this.expression;
}

/**
 * After Effects general tools
 * @namespace
 * @category DuAEF
*/
var DuAE = {};

/**
 * The axis or channels
 * @enum {int}
 * @readonly
*/
DuAE.Axis =
{
	X: 1,
	Y: 2,
	Z: 3,
	RED: 4,
	GREEN: 5,
	BLUE: 6,
	ALPHA: 7,
	HUE: 8,
	SATURATION: 9,
	VALUE: 10
}

/**
 * Types of values
 * @enum {int}
 * @readonly
*/
DuAE.Type =
{
	VALUE: 1,
	SPEED: 2,
	VELOCITY: 3
}

/**
 * Abbreviated units used in the UI.<br />
 * These strings are localized based on internal After Effects dictionnaries.
 * @enum {string}
 * @readonly
*/
DuAE.Unit =
{
	PIXELS: localize("$$$/AE/TLW/GraphEditor/PixelsDest=px"),
	DEGREES: localize("$$$/AE/TLW/GraphEditor/DegreesDest=°"),
	PERCENT: localize("$$$/AE/TLW/GraphEditor/PercentDest=%")
}

/**
 * Units used in the UI<br />
 * These strings are localized based on internal After Effects dictionnaries.
 * @enum {string}
 * @readonly
*/
DuAE.UnitText =
{
	PERCENT: localize("$$$/AE/TLW/GraphEditor/PixelsSource=percent"),
	DEGREES: localize("$$$/AE/TLW/GraphEditor/DegreesSource=degrees"),
	PIXELS: localize("$$$/AE/TLW/GraphEditor/PercentSource=pixels")
}

/**
	* Associative array that converts property match names to their compact English expression statements.
	* @example
	* DuAE.CompactExpression["ADBE Transform Group"]
	* //returns "'transform'"
	* @type {Object}
*/
DuAE.CompactExpression =
{
	"ADBE Transform Group":"'transform'",
	// Handle camera/light vs. AV layers
	"ADBE Anchor Point":"((prop.propertyGroup(prop.propertyDepth).property('intensity')!=null) || (prop.propertyGroup(prop.propertyDepth).property('zoom')!=null)) ? '.pointOfInterest' : '.anchorPoint'",
	"ADBE Position":"'.position'",
	"ADBE Scale":"'.scale'",
	"ADBE Orientation":"'.orientation'",
	"ADBE Rotate X":"'.xRotation'",
	"ADBE Rotate Y":"'.yRotation'",
	// Handle 3D vs. 2D layers
	"ADBE Rotate Z":"(prop.propertyGroup(prop.propertyDepth).threeDLayer || (prop.propertyGroup(prop.propertyDepth).property('intensity')!=null) || (prop.propertyGroup(prop.propertyDepth).property('zoom')!=null)) ? '.zRotation' : '.rotation'",
	"ADBE Opacity":"'.opacity'",
	"ADBE Material Options Group":"'materialOption'",
	"ADBE Casts Shadows":"'.castsShadows'",
	"ADBE Light Transmission":"'.lightTransmission'",
	"ADBE Accepts Shadows":"'.acceptsShadows'",
	"ADBE Accepts Lights":"'.acceptsLights'",
	"ADBE Ambient Coefficient":"'.ambient'",
	"ADBE Diffuse Coefficient":"'.diffuse'",
	"ADBE Specular Coefficient":"'.specular'",
	"ADBE Shininess Coefficient":"'.shininess'",
	"ADBE Metal Coefficient":"'.metal'",
	"ADBE Light Options Group":"'lightOption'",
	"ADBE Light Intensity":"'.intensity'",
	"ADBE Light Color":"'.color'",
	"ADBE Light Cone Angle":"'.coneAngle'",
	"ADBE Light Cone Feather 2":"'.coneFeather'",
	"ADBE Light Shadow Darkness":"'.shadowDarkness'",
	"ADBE Light Shadow Diffusion":"'.shadowDiffusion'",
	"ADBE Camera Options Group":"'cameraOption'",
	"ADBE Camera Zoom":"'.zoom'",
	"ADBE Camera Depth of Field":"'.depthOfField'",
	"ADBE Camera Focus Distance":"'.focusDistance'",
	"ADBE Camera Aperture":"'.aperture'",
	"ADBE Camera Blur Level":"'.blurLevel'",
	"ADBE Text Properties":"'text'",
	"ADBE Text Document":"'.sourceText'",
	"ADBE Text Path Options":"'.pathOption'",
	"ADBE Text Path":"'.path'",
	"ADBE Text Reverse Path":"'.reversePath'",
	"ADBE Text Perpendicular To Path":"'.perpendicularToPath'",
	"ADBE Text Force Align Path":"'.forceAlignment'",
	"ADBE Text First Margin":"'.firstMargin'",
	"ADBE Text Last Margin":"'.lastMargin'",
	"ADBE Text More Options":"'.moreOption'",
	"ADBE Text Anchor Point Option":"'.anchorPointGrouping'",
	"ADBE Text Anchor Point Align":"'.groupingAlignment'",
	"ADBE Text Render Order":"'.fillANdStroke'",
	"ADBE Text Character Blend Mode":"'.interCharacterBlending'",
	"ADBE Text Animators":"'.animator'",
	"ADBE Text Selectors":"'.selector'",
	"ADBE Text Percent Start":"'.start'",
	"ADBE Text Percent End":"'.end'",
	"ADBE Text Percent Offset":"'.offset'",
	"ADBE Text Index Start":"'.start'",
	"ADBE Text Index End":"'.end'",
	"ADBE Text Index Offset":"'.offset'",
	"ADBE Text Range Advanced":"'.advanced'",
	"ADBE Text Range Units":"'.units'",
	"ADBE Text Range Type2":"'.basedOn'",
	"ADBE Text Selector Mode":"'.mode'",
	"ADBE Text Selector Max Amount":"'.amount'",
	"ADBE Text Range Shape":"'.shape'",
	"ADBE Text Selector Smoothness":"'.smoothness'",
	"ADBE Text Levels Max Ease":"'.easeHigh'",
	"ADBE Text Levels Min Ease":"'.easeLow'",
	"ADBE Text Randomize Order":"'.randomizeOrder'",
	"ADBE Text Random Seed":"'.randomSeed'",
	"ADBE Text Selector Mode":"'.mode'",
	"ADBE Text Wiggly Max Amount":"'.maxAmount'",
	"ADBE Text Wiggly Min Amount":"'.minAmount'",
	"ADBE Text Range Type2":"'.basedOn'",
	"ADBE Text Temporal Freq":"'.wigglesSecond'",
	"ADBE Text Character Correlation":"'.correlation'",
	"ADBE Text Temporal Phase":"'.temporalPhase'",
	"ADBE Text Spatial Phase":"'.spatialPhase'",
	"ADBE Text Wiggly Lock Dim":"'.lockDimensions'",
	"ADBE Text Wiggly Random Seed":"'.randomSeed'",
	"ADBE Text Range Type2":"'.basedOn'",
	"ADBE Text Expressible Amount":"'.amount'",
	"ADBE Text Animator Properties":"'.property'",
	"ADBE Text Anchor Point 3D":"'.anchorPoint'",
	"ADBE Text Position 3D":"'.position'",
	"ADBE Text Scale 3D":"'.scale'",
	"ADBE Text Skew":"'.skew'",
	"ADBE Text Skew Axis":"'.skewAxis'",
	"ADBE Text Rotation X":"'.xRotation'",
	"ADBE Text Rotation Y":"'.yRotation'",
	"ADBE Text Rotation":"'.zRotation'",
	"ADBE Text Opacity":"'.opacity'",
	"ADBE Text Fill Opacity":"'.fillOpacity'",
	"ADBE Text Fill Color":"'.fillColor'",
	"ADBE Text Fill Hue":"'.fillHue'",
	"ADBE Text Fill Saturation":"'.fillSaturation'",
	"ADBE Text Fill Brightness":"'.fillBrightness'",
	"ADBE Text Stroke Opacity":"'.strokeOpacity'",
	"ADBE Text Stroke Color":"'.strokeColor'",
	"ADBE Text Stroke Hue":"'.strokeHue'",
	"ADBE Text Stroke Saturation":"'.strokeSaturation'",
	"ADBE Text Stroke Brightness":"'.strokeBrightness'",
	"ADBE Text Stroke Width":"'.strokeWidth'",
	"ADBE Text Line Anchor":"'.lineAnchor'",
	"ADBE Text Line Spacing":"'.lineSpacing'",
	"ADBE Text Track Type":"'.trackingType'",
	"ADBE Text Tracking Amount":"'.trackingAmount'",
	"ADBE Text Character Change Type":"'.characterAlignment'",
	"ADBE Text Character Range":"'.characterRange'",
	"ADBE Text Character Replace":"'.characterValue'",
	"ADBE Text Character Offset":"'.characterOffset'",
	"ADBE Text Blur":"'.blur'",
	"ADBE Mask Parade":"'mask'",
	"ADBE Mask Shape":"'.maskPath'",
	"ADBE Mask Feather":"'.maskFeather'",
	"ADBE Mask Opacity":"'.maskOpacity'",
	"ADBE Mask Offset":"'.maskExpansion'",
	"ADBE Effect Parade":"'effect'",
	"ADBE Paint Group":"'.stroke'",
	"ADBE Paint Shape":"'.path'",
	"ADBE Paint Properties":"'.strokeOption'",
	"ADBE Paint Begin":"'.start'",
	"ADBE Paint End":"'.end'",
	"ADBE Paint Color":"'.color'",
	"ADBE Paint Diameter":"'.diameter'",
	"ADBE Paint Angle":"'.angle'",
	"ADBE Paint Hardness":"'.hardness'",
	"ADBE Paint Roundness":"'.roundness'",
	"ADBE Paint Tip Spacing":"'.spacing'",
	"ADBE Paint Target Channels":"'.channels'",
	"ADBE Paint Opacity":"'.opacity'",
	"ADBE Paint Flow":"'.flow'",
	"ADBE Paint Clone Layer":"'.cloneSource'",
	"ADBE Paint Clone Position":"'.clonePosition'",
	"ADBE Paint Clone Time":"'.cloneTime'",
	"ADBE Paint Clone Time Shift":"'.cloneTimeShift'",
	"ADBE Paint Transform":"'.transform'",
	"ADBE Paint Anchor Point":"'.anchorPoint'",
	"ADBE Paint Position":"'.position'",
	"ADBE Paint Scale":"'.scale'",
	"ADBE Paint Rotation":"'.rotation'",
	"ADBE MTrackers":"'motionTracker'",
	"ADBE MTracker Pt Feature Center":"'.featureCenter'",
	"ADBE MTracker Pt Feature Size":"'.featureSize'",
	"ADBE MTracker Pt Search Ofst":"'.searchOffset'",
	"ADBE MTracker Pt Search Size":"'.searchSize'",
	"ADBE MTracker Pt Confidence":"'.confidence'",
	"ADBE MTracker Pt Attach Pt":"'.attachPoint'",
	"ADBE MTracker Pt Attach Pt Ofst":"'.attachPointOffset'",
	"ADBE Audio Group":"'audio'",
	"ADBE Audio Levels":"'.audioLevels'",
	"ADBE Time Remapping":"'timeRemap'",
	"ADBE Layer Styles":"'layerStyle'",
	"ADBE Blend Options Group":"'.blendingOption'",
		"ADBE Global Angle2":"'.globalLightAngle'",
		"ADBE Global Altitude2":"'.globalLightAltitude'",
		"ADBE Adv Blend Group":"'.advancedBlending'",
		"ADBE Layer Fill Opacity2":"'.fillOpacity'",
		"ADBE R Channel Blend":"'.red'",
		"ADBE G Channel Blend":"'.green'",
		"ADBE B Channel Blend":"'.blue'",
		"ADBE Blend Interior":"'.blendInteriorStylesAsGroup'",
		"ADBE Blend Ranges":"'.useBlendRangesFromSource'",
	"dropShadow/enabled":"'.dropShadow'",
		"dropShadow/mode2":"'.blendMode'",
		"dropShadow/color":"'.color'",
		"dropShadow/opacity":"'.opacity'",
		"dropShadow/useGlobalAngle":"'.useGlobalLight'",
		"dropShadow/localLightingAngle":"'.angle'",
		"dropShadow/distance":"'.distance'",
		"dropShadow/chokeMatte":"'.spread'",
		"dropShadow/blur":"'.size'",
		"dropShadow/noise":"'.noise'",
		"dropShadow/layerConceals":"'.layerKnocksOutDropShadow'",
	"innerShadow/enabled":"'.innerShadow'",
		"innerShadow/mode2":"'.blendMode'",
		"innerShadow/color":"'.color'",
		"innerShadow/opacity":"'.opacity'",
		"innerShadow/useGlobalAngle":"'.useGlobalLight'",
		"innerShadow/localLightingAngle":"'.angle'",
		"innerShadow/distance":"'.distance'",
		"innerShadow/chokeMatte":"'.choke'",
		"innerShadow/blur":"'.size'",
		"innerShadow/noise":"'.noise'",
	"outerGlow/enabled":"'.outerGlow'",
		"outerGlow/mode2":"'.blendMode'",
		"outerGlow/opacity":"'.opacity'",
		"outerGlow/noise":"'.noise'",
		"outerGlow/AEColorChoice":"'.colorType'",
		"outerGlow/color":"'.color'",
		"outerGlow/gradientSmoothness":"'.gradientSmoothness'",
		"outerGlow/glowTechnique":"'.technique'",
		"outerGlow/chokeMatte":"'.spread'",
		"outerGlow/blur":"'.size'",
		"outerGlow/inputRange":"'.range'",
		"outerGlow/shadingNoise":"'.jitter'",
	"innerGlow/enabled":"'.innerGlow'",
		"innerGlow/mode2":"'.blendMode'",
		"innerGlow/opacity":"'.opacity'",
		"innerGlow/noise":"'.noise'",
		"innerGlow/AEColorChoice":"'.colorType'",
		"innerGlow/color":"'.color'",
		"innerGlow/gradientSmoothness":"'.gradientSmoothness'",
		"innerGlow/glowTechnique":"'.technique'",
		"innerGlow/innerGlowSource":"'.source'",
		"innerGlow/chokeMatte":"'.choke'",
		"innerGlow/blur":"'.size'",
		"innerGlow/inputRange":"'.range'",
		"innerGlow/shadingNoise":"'.jitter'",
	"bevelEmboss/enabled":"'.bevelAndEmboss'",
		"bevelEmboss/bevelStyle":"'.style'",
		"bevelEmboss/bevelTechnique":"'.technique'",
		"bevelEmboss/strengthRatio":"'.depth'",
		"bevelEmboss/bevelDirection":"'.direction'",
		"bevelEmboss/blur":"'.size'",
		"bevelEmboss/softness":"'.soften'",
		"bevelEmboss/useGlobalAngle":"'.useGlobalLight'",
		"bevelEmboss/localLightingAngle":"'.angle'",
		"bevelEmboss/localLightingAltitude":"'.altitude'",
		"bevelEmboss/highlightMode":"'.highlightMode'",
		"bevelEmboss/highlightColor":"'.highlightColor'",
		"bevelEmboss/highlightOpacity":"'.highlightOpacity'",
		"bevelEmboss/shadowMode":"'.shadowMode'",
		"bevelEmboss/shadowColor":"'.shadowColor'",
		"bevelEmboss/shadowOpacity":"'.shadowOpacity'",
	"chromeFX/enabled":"'.satin'",
		"chromeFX/mode2":"'.blendMode'",
		"chromeFX/color":"'.color'",
		"chromeFX/opacity":"'.opacity'",
		"chromeFX/localLightingAngle":"'.angle'",
		"chromeFX/distance":"'.distance'",
		"chromeFX/blur":"'.size'",
		"chromeFX/invert":"'.invert'",
	"solidFill/enabled":"'.colorOverlay'",
		"solidFill/mode2":"'.blendMode'",
		"solidFill/color":"'.color'",
		"solidFill/opacity":"'.opacity'",
	"gradientFill/enabled":"'.gradientOverlay'",
		"gradientFill/mode2":"'.blendMode'",
		"gradientFill/opacity":"'.opacity'",
		"gradientFill/gradientSmoothness":"'.gradientSmoothness'",
		"gradientFill/angle":"'.angle'",
		"gradientFill/type":"'.style'",
		"gradientFill/reverse":"'.reverse'",
		"gradientFill/align":"'.alignWithLayer'",
		"gradientFill/scale":"'.scale'",
		"gradientFill/offset":"'.offset'",
	"patternFill/enabled":"'.patternOverlay'",
		"patternFill/mode2":"'.blendMode'",
		"patternFill/opacity":"'.opacity'",
		"patternFill/align":"'.linkWithLayer'",
		"patternFill/scale":"'.scale'",
		"patternFill/phase":"'.offset'",
	"frameFX/enabled":"'.stroke'",
		"frameFX/mode2":"'.blendMode'",
		"frameFX/color":"'.color'",
		"frameFX/size":"'.size'",
		"frameFX/opacity":"'.opacity'",
		"frameFX/style":"'.position'",
	"ADBE Layer Overrides":"'masterProperty'"
};

/**
	* Menu Command Ids<br />
	* These are the ids which can be found using <code>app.findMenuCommandID("insertMenuNameHere");</code><br />
	* Use <code>app.executeCommand(id)</code> to run them.
	* @enum {int}
	* @readonly
	* @example
	* app.executeCommand(DuAE.MenuCommandID.COPY); //copies the selection
	* app.executeCommand(DuAE.MenuCommandID.PASTE); //pastes the selection
*/
DuAE.MenuCommandID =
{
	//EDIT menu
	CUT: 18,
	COPY: 19,
	COPY_WITH_PROPERTY_LINKS: 10310,
	PASTE: 20,
	DUPLICATE: 2080,
	UNDO: 16,
	GENERAL_PREFERENCES: 2359,
	SCRIPTING_PREFERENCES: 3131,
	LAYER_CONTROLS: 2435,
	REVEAL_EXPRESSION_ERRORS: 2731,
	CONVERT_AUDIO_TO_KEYFRAMES: 4218
}

/**
	* How to place new layers
	* @enum {int}
	* @readonly
*/
DuAE.LayerPlacement =
{
	BOTTOM: 0,
	UNDER_LAYER: 1,
	ABOVE_LAYER: 2,
	TOP: 3
}

/**
 * Types of layers used by Ae
 * @enum {string}
 * @readonly
*/
DuAE.LayerType =
{
	NULL: 'null',
	SOLID: 'solid',
	SHAPE: 'shape',
	TEXT: 'text',
	ADJUSTMENT: 'adjustment',
	LIGHT: 'light',
	CAMERA: 'camera'
}

/**
 * Attributes of layers in Ae
 * @enum {string}
 * @readonly
*/
DuAE.LayerAttribute =
{
	SELECTED: 'selected',
	VISIBLE: 'visible',
	AUDIO: 'audio',
	SOLO: 'solo',
	LOCKED: 'locked',
	SHY: 'shy',
	EFFECTS_ENABLED: 'effects',
	MOTION_BLUR: 'motionblur',
	THREE_D: '3d',
	GUIDE: 'guide'
}

/**
 * Loop types
 * @enum {string}
 * @readonly
*/
DuAE.LoopType = 
{
	HOLD: 'hold',
	NONE: 'none',
	CYCLE: 'cycle',
	PINGPONG: 'pingpong',
	OFFSET: 'offset',
	CONTINUE: 'continue'
}

/**
 * How to align in time
 * @enum {string}
 * @readonly
*/
DuAE.TimeAlignment = 
{
	CENTER: 'center',
	IN_POINT: 'in',
	OUT_POINT: 'out'
}

/**
 * The type of AE preferences
 * @enum {int}
 * @readonly
*/
DuAE.PrefType =
{
	BOOL: 0,
	STRING: 1,
	FLOAT: 2,
	LONG: 3
}

/**
 * List of file extensions whih may be imported as several items with the same source
 * @type {DuList}
 */
DuAE.DuAELayeredFileType = new DuList( ["psd","psb","ai","fla"] );

/**
	* Enum for selections
	* @readonly
	* @static
	* @enum {int}
	*/
DuAE.SelectionMode = {
    SELECTED_PROPERTIES: 0,
    SELECTED_LAYERS: 1,
    ACTIVE_COMPOSITION: 2,
    SELECTED_COMPOSITIONS: 3,
    ALL_COMPOSITIONS: 4
}

/**
 * Default color labels
 * @readonly
 * @todo get real labels (from pref file?)
 */
DuAE.ColorLabels = [];
DuAE.ColorLabels[0]  = [0, 0, 0 ];	// 0. None
DuAE.ColorLabels[1]  = [121/255, 058/255, 058/255 ];	// 1. Red
DuAE.ColorLabels[2]  = [144/255, 138/255, 068/255 ];	// 2. Yellow
DuAE.ColorLabels[3]  = [115/255, 132/255, 130/255 ];	// 3. Aqua
DuAE.ColorLabels[4]  = [145/255, 124/255, 131/255 ];	// 4. Pink
DuAE.ColorLabels[5]  = [115/255, 115/255, 131/255 ];	// 5. Lavender
DuAE.ColorLabels[6]  = [146/255, 127/255, 109/255 ];	// 6. Peach
DuAE.ColorLabels[7]  = [120/255, 130/255, 120/255 ];	// 7. Sea Foam
DuAE.ColorLabels[8]  = [082/255, 093/255, 142/255 ];	// 8. Blue
DuAE.ColorLabels[9]  = [067/255, 112/255, 068/255 ];	// 9. Green
DuAE.ColorLabels[10] = [101/255, 052/255, 107/255 ];	// 10. Purple
DuAE.ColorLabels[11] = [146/255, 103/255, 037/255 ];	// 11. Orange
DuAE.ColorLabels[12] = [094/255, 065/255, 051/255 ];	// 12. Brown
DuAE.ColorLabels[13] = [152/255, 085/255, 137/255 ];	// 13. Fuchsia
DuAE.ColorLabels[14] = [061/255, 111/255, 113/255 ];	// 14. Cyan
DuAE.ColorLabels[15] = [114/255, 105/255, 090/255 ];	// 15. Sandstone
DuAE.ColorLabels[16] = [045/255, 062/255, 045/255 ];	// 16. DarkGreen

/**
 * Gets the compact expression synonym of a matchName
 * @param {string} matchName The matchName of a property
 * @param {string} [name] A replacement name (or index) in case the compact expression does not exist. If omitted, the matchName will be used.
 * @param {Proeprty} [prop] The original property; may be needed to differenciate between 3D layers / cam / lights, etc
 */
DuAE.getCompactExpression = function( matchName, name, prop )
{
	name = def(name, '');
	var translatedName = DuAE.CompactExpression[ matchName ];

	if ( isdef( translatedName ) )
		return eval( translatedName );
	else if (name != '')
		return ( "(" + name + ")" );
	else
		return ( "(" + matchName + ")" );
}

/**
 * Checks if After Effects has the given preference
 * @param {string} section The section of the preferences
 * @param {string} key The key
 * @param {PREFType} [file=PREFType.PREF_Type_MACHINE_SPECIFIC] The preference file, from the <code>PREFType</code> enum value of the After Effects API
 * @returns {Boolean} true if the pref exists
 */
DuAE.hasPref = function( section, key, file)
{
	app.preferences.saveToDisk();
	app.preferences.reload();
	file = def(file, PREFType.PREF_Type_MACHINE_SPECIFIC);
	return app.preferences.havePref(section, key, file);
}

/**
 * Gets a pref
 * @param {string} section The section of the preferences
 * @param {string} key The key
 * @param {PREFType} [file=PREFType.PREF_Type_MACHINE_SPECIFIC] The preference file, from the <code>PREFType</code> enum value of the After Effects API
 * @param {DuAE.PrefType} [type=DuAE.PrefType.STRING] The type of the preference to return
 * @returns {any|null} The pref or null if it does not exists
 */
DuAE.getPref = function( section, key, file, type)
{
	try // Won't work without file access
	{
		app.preferences.saveToDisk();
		app.preferences.reload();
	}
	catch (e) {}
	
	file = def(file, PREFType.PREF_Type_MACHINE_SPECIFIC);

	if ( !DuAE.hasPref( section, key, file) ) return null;
	type = def(type, DuAE.PrefType.STRING);
	if (type == DuAE.PrefType.STRING) return app.preferences.getPrefAsString(section, key, file);
	if (type == DuAE.PrefType.LONG) return app.preferences.getPrefAsLong(section, key, file);
	if (type == DuAE.PrefType.FLOAT) return app.preferences.getPrefAsFloat(section, key, file);
	if (type == DuAE.PrefType.BOOL) return app.preferences.getPrefAsBool(section, key, file);
}

/**
	* Gets the DuAEProperty for the properties
	* @param {PropertyBase[]} props - The Properties
	* @return {DuAEProperty[]} The info
*/
DuAE.getDuAEProperty = function (props)
{
	//convert to propinfo
	var propInfos = [];
	for (var i = 0 ; i < props.length ; i++)
	{
		var propInfo;
		if (props[i] instanceof DuAEProperty) propInfo = props[i];
		else propInfo = new DuAEProperty(props[i]);
		propInfos.push(propInfo);
	}
	return propInfos;
}

/**
	* Checks if the file is a layered type (psd, ai, psb, fla...)
	* @param {String|File}	 [file]	- The file or the path
	* @return {boolean} true if it's a layered file
*/
DuAE.isLayeredFile = function(file)
{
	var ext = DuPath.getExtension(file);
	return DuAE.DuAELayeredFileType.contains( ext.toLowerCase() );
}

/**
 * Informations about the version of after effects.
 * @type {DuVersion}
 * @readonly
 */
DuAE.version = new DuVersion( app.version );

/**
 * Checks if the javascript debugger is enabled
 * @return {Boolean} true if the debugger is enabled.
 */
DuAE.isDebuggerEnabled = function()
{
	return DuAE.getPref( "Main Pref Section v2", "Pref_JAVASCRIPT_DEBUGGER", PREFType.PREF_Type_MACHINE_INDEPENDENT, DuAE.PrefType.LONG) == 1;
}

/**
 * Gets the public name of a version of After Effects (like CC2015.3 for version 13.8)
 * @param {float}	[versionAsFloat]	- The version as a float. If not provided, will default to the current version of the running instance of After Effects.
 * @return {string}	The version name.
 */
DuAE.getAEVersionName = function ( versionAsFloat ) {
    if ( versionAsFloat == undefined ) versionAsFloat = DuAE.version.version;
    if ( versionAsFloat < 8 ) return "" + versionAsFloat;
    if ( versionAsFloat >= 8 && versionAsFloat < 9 ) return "CS3";
    if ( versionAsFloat >= 9 && versionAsFloat < 10 ) return "CS4";
    if ( versionAsFloat >= 10 && versionAsFloat < 10.5 ) return "CS5";
    if ( versionAsFloat >= 10.5 && versionAsFloat < 11 ) return "CS5.5";
    if ( versionAsFloat >= 11 && versionAsFloat < 12 ) return "CS6";
    if ( versionAsFloat >= 12 && versionAsFloat < 13 ) return "CC";
    if ( versionAsFloat >= 13 && versionAsFloat < 13.1 ) return "CC2014";
    if ( versionAsFloat >= 13.1 && versionAsFloat < 13.2 ) return "CC2014.1";
    if ( versionAsFloat >= 13.2 && versionAsFloat < 13.5 ) return "CC2014.2";
    if ( versionAsFloat >= 13.5 && versionAsFloat < 13.6 ) return "CC2015";
    if ( versionAsFloat >= 13.6 && versionAsFloat < 13.7 ) return "CC2015.1";
    if ( versionAsFloat >= 13.7 && versionAsFloat < 13.8 ) return "CC2015.2";
    if ( versionAsFloat >= 13.8 && versionAsFloat < 14 ) return "CC2015.3";
    if ( versionAsFloat >= 14 && versionAsFloat < 15 ) return "CC2017";
    if ( versionAsFloat >= 15 && versionAsFloat < 16 ) return "CC2018";
    if ( versionAsFloat >= 16 && versionAsFloat < 17 ) return "CC2019";
    if ( versionAsFloat >= 17 && versionAsFloat < 18 ) return "2020";
    if ( versionAsFloat >= 18 && versionAsFloat < 22) return "2021";
    if ( versionAsFloat >= 22 ) return "2022";
    return "Unknown";
}

/**
 * Gets the version of After Effects from its public name (like 13.8 for CC2015.3)
 * @param {float}	[versionAsFloat]	- The version name.
 * @return {DuVersion}	The version.
 */
DuAE.getAEVersion = function ( versionName ) {
    if (versionName == "CS3") return new DuVersion( "8.0" );
    if (versionName == "CS4") return new DuVersion( "9.0" );
    if (versionName == "CS5") return new DuVersion( "10.0" );
    if (versionName == "CS5.5") return new DuVersion( "10.5" );
    if (versionName == "CS6") return new DuVersion( "11.0" );
    if (versionName == "CC") return new DuVersion( "12.0" );
    if (versionName == "CC2014") return new DuVersion( "13.0" );
    if (versionName == "CC2014.1") return new DuVersion( "13.1" );
    if (versionName == "CC2014.2") return new DuVersion( "13.2" );
    if (versionName == "CC2015") return new DuVersion( "13.5" );
    if (versionName == "CC2015.1") return new DuVersion( "13.6" );
    if (versionName == "CC2015.2") return new DuVersion( "13.7" );
    if (versionName == "CC2015.3") return new DuVersion( "13.8" );
    if (versionName == "CC2017") return new DuVersion( "14.0" );
    if (versionName == "CC2018") return new DuVersion( "15.0" );
    if (versionName == "CC2019") return new DuVersion( "16.0" );
    if (versionName == "2020") return new DuVersion( "17.0" );
    if (versionName == "2021") return new DuVersion( "18.0" );
    if (versionName == "2022") return new DuVersion( "22.0" );
    return new DuVersion(versionName);
}

/**
 * Checks if the current version is higher than a given one
 * @param {string} versionName The minimum version
 * @return {boolean} True if the current version is higher (strict, will be false if they're equal)
 */
DuAE.isVersionHigherThan = function (versionName)
{
    return DuAE.version.higherThan( DuAE.getAEVersion( versionName ) );
}

/**
 * Checks if the current version is at least a given one
 * @param {string} versionName The minimum version
 * @return {boolean} True if the current version is higher or the same
 */
DuAE.isVersionAtLeast = function (versionName)
{
    return DuAE.version.atLeast( DuAE.getAEVersion( versionName ) );
}

/**
 * Gets the aerender binary
 * @return {File|null} The aerender binary, or null if not found
 */
DuAE.getAeRender = function()
{
    var packageFolder = Folder.appPackage;
    var aeRenderPath = packageFolder.absoluteURI + "/aerender";
    if (DuSystem.win) aeRenderPath += ".exe"
    var aeRenderFile = new File(aeRenderPath)
    if (aeRenderFile.exists) return aeRenderFile;
    else return null;
}

//low-level undocumented flag.
//true if an undo group has been opened and has not been closed yet.
DuAE.openedUndoGroup = false;

//low-level undocumented flag.
//the name of the currently opened undo group
DuAE.undoGroupName = '';

/**
 * Begins an undoGroup.<br />
 * Automatically prepend the group name with the script name.<br />
 * Using this method is safer than the native one, as DuAEF will try to avoid opening several undo groups at once.<br />
 * The group name is translatable.
 * @param {string} groupName - The name of the Undo Group.
 * @param {boolean} [autoClose=true] - By default, DuAEF will close any previously opened undogroup (which has a different name) to prevent any error.<br />
 * Set this to <code>false</code> in order to ignore this new group beginning and keep the previously opened one.
 */
DuAE.beginUndoGroup = function ( groupName, autoClose ) {
    groupName = def( groupName, "" );
    autoClose = def( autoClose, true );

    if ( DuAE.openedUndoGroup ) {
        if ( autoClose && DuAE.undoGroupName != groupName ) DuAE.endUndoGroup();
        else return;
    }
    app.beginUndoGroup( DuESF.scriptName + ' | ' + groupName );

    DuAE.undoGroupName = groupName;

    DuAE.openedUndoGroup = true;
}

/**
 * Ends an undoGroup.<br />
 * Using this method is safer than the native one, as DuAEF will try to avoid opening several undo groups at once.
 * @param {string} [groupName] - The name of the Undo Group to end. Use this if you used  {@link DuAE.beginUndoGroup} with the 'autoClose' argument set to false before.
 */
DuAE.endUndoGroup = function (groupName) {
    groupName = def(groupName, "");
    if (groupName != "" && DuAE.undoGroupName != groupName) return;
    app.endUndoGroup();
    DuAE.openedUndoGroup = false;
}

/**
 * Runs app.executeCommand in a safer way, taking care of undogroups.
 * @param {int|string} commandID - The ID of the command as given by app.findMenuCommandID(), or if it is a string, the name of the command.
 */
DuAE.executeCommand = function ( commandID, ignoreUndoGroups ) {
    ignoreUndoGroups = def(ignoreUndoGroups, false);
	if ( commandID === "" ) return;
    if ( jstype( commandID ) == 'string' ) commandID = app.findMenuCommandId( commandID );
    //the command will mess undo groups up if there already is one opened.
    openedUndoGroup = DuAE.openedUndoGroup;
    if (!ignoreUndoGroups) DuAE.endUndoGroup();
    app.executeCommand( commandID );
    if ( openedUndoGroup && !ignoreUndoGroups ) DuAE.beginUndoGroup( DuAE.undoGroupName );
}

/**
 * Opens a ScriptUI Panel if it is installed, or displays an alert otherwise.
 * @param {string} panelScriptName - The Script name of the panel "script.jsx"
 */
DuAE.openScriptUIPanel = function ( panelScriptName ) {
    if ( panelScriptName == "" ) return;
    var scriptUIFile = new File( Folder.appPackage.absoluteURI + '/Scripts/ScriptUI Panels/' + panelScriptName );
    if ( !scriptUIFile.exists ) {
        scriptUIFile = new File( Folder.userData.absoluteURI + '/Adobe/After Effects/' + DuAE.version.versionString + '/Scripts/ScriptUI Panels/' + panelScriptName );
    }
    if ( !scriptUIFile.exists ) {
        alert( "The panel " + panelScriptName + " is not installed." );
        return;
    }
    DuAE.executeCommand( panelScriptName );
}

/**
 * Cuts the selection (runs the cut menu command)
 */
DuAE.cut = function () {
    DuAE.executeCommand( DuAE.MenuCommandID.CUT );
}

/**
 * Copies the selection (runs the copy menu command)
 */
DuAE.copy = function () {
    DuAE.executeCommand( DuAE.MenuCommandID.COPY );
}

/**
 * Duplcates the selection (runs the duplicate menu command)
 */
DuAE.duplicate = function () {
    DuAE.executeCommand( DuAE.MenuCommandID.DUPLICATE );
}

/**
 * Copies the selection with property links (runs the copy menu command)<br />
 * Only on versions of After Effects greater than 11.0 (CS6)<br />
 * On CS6 and below, a standard copy will be done.
 */
DuAE.copyWithPropertyLinks = function () {
    if ( DuAE.version >= 12.0 ) DuAE.executeCommand( DuAE.MenuCommandID.COPY_WITH_PROPERTY_LINKS );
    else DuAE.copy();
}

/**
 * Pastes the selection (runs the paste menu command)
 */
DuAE.paste = function () {
    DuAE.executeCommand( DuAE.MenuCommandID.PASTE );
}

/**
 * Undoes (runs the undo command)
 */
DuAE.undo = function () {
    DuAE.executeCommand( DuAE.MenuCommandID.UNDO );
}

/**
 * Checks if the Folder is an Auto-Save folder
 * @param {Folder|string} folder The folder or path to check
 * @return {boolean} true if the folder is an auto-save folder 
 */
DuAE.isAutoSaveFolder = function ( folder ) {
    if (jstype(folder) == 'string' ) folder = new Folder(folder);
    if (!(folder instanceof Folder)) {
        DuDebug.throwTypeError( folder, 'folder', 'Folder/string', 'DuAE.isAutoSaveFolder( folder )');
        return false;
    }

    var folderName = folder.fsName.toLowerCase();
    if (DuSystem.win) folderName = folderName.split('\\').pop();
    else folderName = folderName.split('/').pop();
    
    var aaeString = 'adobe after effects';
    var autosaveString = localize("$$$/AE/MenuID/0307/AutoSave=Auto-Save").toLowerCase();

    return folderName.indexOf( aaeString ) >= 0 && folderName.indexOf( autosaveString ) >= 0;
}

/**
 * Shows/hides the layer controls
 */
DuAE.toggleLayerControls = function ( ) {
	app.executeCommand( DuAE.MenuCommandID.LAYER_CONTROLS );
}

/**
 * Gets the list of folders where scripts may be installed
 * @param {string} [suboflder] The name of a subfolder, like "ScriptUI Panels" or "Startup" or "Shutdown"...
 * @return {Folder[]} the list of Folder objects.
 */
DuAE.scriptFolders = function( subfolder ) {
	subfolder = def(subfolder, "");

	var folders = [];
	
	// Get in Support files
	var supportFilesPath;
	if (DuSystem.mac) supportFilesPath = Folder.appPackage.parent.absoluteURI;
	else supportFilesPath = Folder.appPackage.absoluteURI;
	var supportFiles = new Folder (
		supportFilesPath + "/Scripts/" + subfolder
	);

	if (supportFiles.exists) folders.push( supportFiles);

	// Get in appdata
	// Try in the main version first
	var appDataPath;
	if (DuSystem.mac) appDataPath = Folder.userData.parent.absoluteURI + "/Preferences";
	else appDataPath = Folder.userData.absoluteURI;
	var appDataFiles = new Folder(
		appDataPath + "/Adobe/After Effects/" + DuAE.version.major + "." + DuAE.version.minor + "/Scripts/" + subfolder
	);

	// Try the Beta
	if (!appDataFiles.exists) appDataFiles = new Folder(
		Folder.userData.absoluteURI + "/Adobe/After Effects (Beta)/" + DuAE.version.major + "." + DuAE.version.minor + "/Scripts/" + subfolder
	);

	if (appDataFiles.exists) folders.push( appDataFiles );

	return folders;
}

// ========== DEPRECATED ==========

/**
	* Converts an AE Collection to an Array<br />
	* This method is deprecated, you should use a {@link DuList} otherwise
	* @deprecated
	* @param {Array|Collection} collection - The collection to convert
	* @return {Array} The array
*/
DuAE.convertCollectionToArray = function (collection)
{
	var arr = [];
	if (DuList.isAECollection(collection))
	{
		for (var i = 1;i<=collection.length;i++)
		{
			arr.push(collection[i]);
		}
	}
	else
	{
		arr = collection;
	}
	return arr;
}// ==================== |-----| ====================
// ==================== | tag | ====================
// ==================== |-----| ====================

/**
 * After Effects tag methods<br />
 * Tags are markers set on the first frame of layers, displaying an info about the layer (usually, a "type" or tag).<br />
 * These markers are used by DuAEF (and Duik, DuGR, ...) to recognise and manipulate the layers, and to store hidden data.<br />
 * They're also used assign the layer to groups, which can be used by other scripts, especially DuGR.
 * @namespace
 * @category DuAEF
 */
var DuAETag = {};

/**
 * The list of paramaters which can be set by DuAEF in tags.
 * @enum {string}
 * @readonly
 */
DuAETag.Key = {
    /**
     * Used to temporarily store the list of children of the layers.
     */
    CHILD_LAYERS: "childrenLayers",
    /**
     * A list of groups this layer belongs to.
     */
    GROUPS: "groups"
}

/**
 * The list of names used for the markers.<br />
 * Names are the string shown to the user on the marker, the comment.<br />
 * Note that these names may change as they may be localized and should not be used to manipulate layers, but only shown to the user.
 * @enum {string}
 * @readonly
 */
DuAETag.Name = {
    /**
     * A layer toggled to edit mode. 
     * @default "Edit mode"
     */
    EDIT_MODE: "Edit mode"
}

/**
 * The list of types to parse values.
 * @enum {int}
 * @readonly
 */
DuAETag.Type = {
    STRING: 0,
    BOOL: 1,
    INT: 2,
    ARRAY: 3,
    FLOAT: 4
}

/**
 * Set to true so the markers are created before the composition start time and hidden
 */
DuAETag.hideTags = false;

// low-level undocumented function to get the time the marker should be set on
DuAETag.getTagTime = function( layer )
{
    var markerProp = layer.property( 'ADBE Marker' );
    var time = 0;
    if ( DuAETag.hideTags ) time = -60; // Starting 60 s before start time should be enough!
    if (markerProp.numKeys > 0)
    {
        // Get an empty frame to set the tag
        var frame = 0;
        if ( DuAETag.hideTags ) frame = -1000; // Starting 1000 frames before start time should be enough!
        for( var i = 1, n = markerProp.numKeys; i <= n; i++)
        {
            var kT = markerProp.keyTime(i);
            var kF = DuAEComp.timeToFrames( kT );
            // Frame 0 is available
            if (kF > frame) break;
            if (kF > frame + 10)
            {
                frame = frame + 10;
                break;
            }
            if (kF < frame) continue;
            frame = frame + 10;
        }
        time = DuAEComp.framesToTime( frame );
    }
    return time;
}

/**
 * Sets a new marker/tag (or gets the existing one) on the first frame of the layer.
 * @param {Layer} layer - The layer to set the tag on.
 * @param {string} [tagName] - The name to display on the marker (the comment of the marker)
 * @param {MarkerValue} [tag] - An existing tag.
 * @param {Boolean} [hidden=DuAETag.hideTags] - When true, the marker will be created before the layer and comp start time, so it's hidden.
 * @return {MarkerValue} The marker (tag), with an extra property <code>keyIndex</code> which is the index of the corresponding keyframe.
 */
DuAETag.set = function( layer, tagName, tag )
{   
    var markerProp = layer.property( 'ADBE Marker' );

    tag = def( tag, DuAETag.get( layer ) );

    // Create tag if needed
    if (!tag)
    {
        var time = DuAETag.getTagTime(layer);
        var comment = def(tagName, '');
        tag = new MarkerValue( comment );
        tag.keyTime = time;
        tag.keyIndex = -1;
    }
    // Otherwise just set comment
    else 
    {
        if (isdef( tagName )) tag.comment = tagName;
    }
    
    var tagParams = tag.getParameters();
    tagParams.duaef = def( tagParams.duaef, true);
    tag.setParameters( tagParams );

    if ( tag.keyIndex > 0 )
    {
        markerProp.setValueAtKey( tag.keyIndex, tag );
    }
    else 
    {
        markerProp.setValueAtTime( tag.keyTime, tag );
        tag.keyIndex = markerProp.nearestKeyIndex( time );
    }
    
 
    return tag;
}

/**
 * Gets the tag.
 * @param {Layer} layer - The layer to get the tag from.
 * @return {MarkerValue|null} The marker (tag) or <code>null</code> if not found.
 */
DuAETag.get = function( layer )
{
    if ( !layer ) return null;

    var markerProp = layer.property( 'ADBE Marker' );

    for ( var i = 1, num = markerProp.numKeys; i <= num; i++ )
    {
        var tag = markerProp.keyValue( i );
        tag.keyIndex = i;
        tag.keyTime = markerProp.keyTime( i );
        var params = tag.getParameters();
        if ( !params.duaef ) continue;
        // check time to shoz / hide
        if ( ( DuAETag.hideTags && tag.keyTime >= 0 ) || ( !DuAETag.hideTags && tag.keyTime < 0))
        {
            var time = DuAETag.getTagTime(layer);
            markerProp.removeKey( tag.keyIndex );
            markerProp.setValueAtTime( time, tag );
            tag.keyTime = time;
            tag.keyIndex = markerProp.nearestKeyIndex( time );
        }
        return tag;
    }
    return null;
}

/**
 * Removes the tag from the layer
 * @param {Layer} layer - The layer containing the tag.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 */
DuAETag.remove = function( layer, tag )
{
    tag = def(tag, DuAETag.get( layer ));
    if ( !tag ) return;

    var markerProp = layer.property( 'ADBE Marker' );
    markerProp.removeKey( tag.keyIndex );
}

/**
 * Sets the name (comment of the marker) of the tag.
 * @param {Layer} layer The layer to get the tag from
 * @param {string} [tagName] - The name to display on the marker (the comment of the marker)
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 * @return {string} The name.
 */
DuAETag.setName = function ( layer, tagName, tag )
{
    tag = def(tag, DuAETag.get( layer ));
    if (!tag) DuAETag.set( layer, tagName )
    else 
    {
        tag.comment = tagName;
        var markerProp = layer.property( 'ADBE Marker' );
        markerProp.setValueAtKey( tag.keyIndex, tag );
    }
}

/**
 * Gets the name (comment of the marker) of the tag.
 * @param {Layer} layer The layer to get the tag from
 * @return {string} The name.
 */
DuAETag.getName = function ( layer )
{
    var tag = DuAETag.get( layer );
    if (!tag) return "";
    return tag.comment;
}

/**
 * Sets a new parameter (a key/value pair) to the hidden parameters stored in the tag.
 * @param {Layer} layer - The layer to get the tag from.
 * @param {string} key - The key. May be one of {@link DuAETag.Key}.
 * @param {string} value - The value, which needs to be a string.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 * @return {Boolean} Success, may be false if there's no tag on this layer yet.
 */
DuAETag.setValue = function( layer, key, value, tag )
{
    tag = def(tag, DuAETag.set( layer ));
    if (!tag) return;

    var tagParams = tag.getParameters();
    tagParams.duaef = def( tagParams.duaef, true);

    tagParams[ key ] = value;
    tag.setParameters( tagParams );

    layer.property( 'ADBE Marker' ).setValueAtKey( tag.keyIndex, tag );
}

/**
 * Gets the value of a specific key in the tag parameters.
 * @param {Layer} layer - The layer to get the tag from.
 * @param {string} key - The key. May be one of {@link DuAETag.Key}.
 * @param {DuAETag.Type} [type=DuAETag.Type.STRING] - The expected type for the value.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 * @return {any} The value, null if the key was not found.
 */
DuAETag.getValue = function( layer, key, type, tag )
{
    tag = def(tag, DuAETag.get( layer ));
    if (!tag) return null;

    type = def( type, DuAETag.Type.STRING);

    var params = tag.getParameters();
    if (!params) return null;

    var val = params[ key ];
    if (!isdef( val )) return null;
    if (type == DuAETag.Type.STRING) return val;
    if (type == DuAETag.Type.BOOL)
    {
        if (val === 'false') return false;
        else if (val === 'true') return true;
        else if (val === '') return false;
        else if (parseInt(val) == 0) return false;
        else return true;
    }
    if (type == DuAETag.Type.INT)
    {
        if (val == '') return 0;
        return parseInt(val);
    }
    if (type == DuAETag.Type.FLOAT)
    {
        if (val == '') return 0;
        return parseFloat(val);
    }
    if (type == DuAETag.Type.ARRAY)
    {
        if (val == '') return [];
        return val.split(',');
    }

    return null;
}

/**
 * Gest the list of the groups this layer belongs to.
 * @param {Layer} layer
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 * @returns {string[]} The list of groups. May be an empty list.
 */
DuAETag.getGroups = function( layer, tag )
{
    tag = def(tag, DuAETag.get( layer ));

    var groups = DuAETag.getValue( layer, DuAETag.Key.GROUPS, DuAETag.Type.ARRAY, tag );
    if (!groups) return [];
    return groups;
}

/**
 * Assigns the layer to a group.<br >
 * If the layer does not have a tag yet, a new one will be created with the group name.
 * @param {Layer} layer The layer.
 * @param {string} groupName The group.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 */
DuAETag.addGroup = function (layer, groupName, tag )
{
    tag = def(tag, DuAETag.set( layer ));

    var groups = DuAETag.getGroups( layer, tag )
    if (!groups) groups = [];

    groups = new DuList(groups);
    if (groups.indexOf(groupName) >= 0) return;

    groups.push( groupName );

    DuAETag.setValue( layer, DuAETag.Key.GROUPS, groups.join(','), tag );
    DuAETag.setName( layer, groups.join(' | '), tag );
}

/**
 * Unassigns the layer from a group.
 * @param {Layer} layer The layer.
 * @param {string} groupName The group.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 */
DuAETag.removeGroup = function (layer, groupName, tag )
{
    tag = def(tag, DuAETag.get( layer ));

    var groups = DuAETag.getGroups( layer, tag);
    if(!groups) return;

    groups = new DuList( groups );
    if (groups.indexOf(groupName) < 0) return;
    groups.removeAll( groupName );
    DuAETag.setValue( layer, DuAETag.Key.GROUPS, groups.join(','), tag );
    DuAETag.setName( layer, groups.join(' | '), tag );
}

/**
 * Renames a group
 * @param {Layer} layer The layer.
 * @param {string} previousName The current name
 * @param {string} newName The new name
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 */
DuAETag.renameGroup = function( layer, previousName, newName, tag )
{
    tag = def(tag, DuAETag.get( layer ));

    var groups = DuAETag.getGroups( layer, tag);
    if(!groups) return;

    groups = new DuList( groups );
    groups.replace( previousName, newName );
    DuAETag.setValue( layer, DuAETag.Key.GROUPS, groups.join(','), tag );
    DuAETag.setName( layer, groups.join(' | '), tag );
}// ==================== |----------| ====================
// ==================== | property | ====================
// ==================== |----------| ====================

/**
 * Constructs a new DuAEProperty
 * @example
 * var propInfo = new DuAEProperty(property);
 * layer("ADBE effect parade").addProperty("ADBE layer control"); //now the property object is broken
 * property = propInfo.getProperty(); // You can retrieve the property like this, fixed if it's an effect
 * @class DuAEProperty
 * @classdesc Get some handy informations about a property<br />
 * This class is able to "fix" effects properties which have been broken by
 * the addition of another effect on the same layer, as long as the class has been
 * instanciated before the effect has been broken.<br />
 * Some methods in this class (but not all) are recursive: they can be run even if the property is a group containing other properties.
 * @param {PropertyBase|DuAEProperty} property - The property. If a DuAEProperty is provided, the constructor returns it (it does not make a copy).<br />
 * This makes it easy to avoid type checking, as you can always pass any property or DuAEProperty to the constructor to be sure to handle a DuAEProperty, without any impact on performance.<br />
 * @example
 * myFunction (prop) //This function can be passed either a property or a DuAEProperty
 * {
 *   propInfo = new DuAEProperty(prop);
 *   prop = propInfo.getProperty();
 * }
 * @category DuAEF
 */
function DuAEProperty(property) {
    if (property instanceof DuAEProperty) return property;
    if (!isdef( property )) {
        DuDebug.throwTypeError(property, "property", "Property", "DuAEProperty(property)");
        return;
    }

    var expressionEnabled = property.expressionEnabled;

    this.property = property;
    /**
     * The original name of the property, same as DuAEProperty.getProperty().name
     * @memberof DuAEProperty.prototype
     * @name name
     * @readonly
     * @type {string}
     */
    this.name = property.name;
    /**
     * The original matchName of the property, same as DuAEProperty.getProperty().matchName
     * @memberof DuAEProperty.prototype
     * @name matchName
     * @readonly
     * @type {string}
     */
    this.matchName = property.matchName;
    /**
     * The original property index of the property, same as DuAEProperty.getProperty().propertyIndex
     * @memberof DuAEProperty.prototype
     * @name name
     * @readonly
     * @type {int}
     */
    this.index = property.propertyIndex;
    /**
     * Is this an effect? same as DuAEProperty.getProperty().isEffect
     * @memberof DuAEProperty.prototype
     * @name isEffect
     * @readonly
     * @type {Boolean}
     */
    this.isEffect = property.isEffect;
    /**
     * The containing effect, if any.
     * @memberof DuAEProperty.prototype
     * @name effect
     * @readonly
     * @type {PropertyGroup|null}
     */
    this.effect = null;
    if (this.isEffect) this.effect = property;
    /**
     * The containing layer
     * @memberof DuAEProperty.prototype
     * @name layer
     * @readonly
     * @type {Layer}
     */
    this.layer = null;

    // Get layer and effect
    // And generate some kind of unique ID
    var parentProp = property;
    this.parentIndices = [];
    var id = '';
    while (parentProp.parentProperty) {
        var isEffect = parentProp.isEffect;
        if (isEffect) {
            this.isEffect = true;
            this.effect = parentProp;
        }

        // If not the layer, keep index
        this.parentIndices.unshift(parentProp.propertyIndex);
        
        id += parentProp.propertyIndex;

        // Traverse up the property tree
        parentProp = parentProp.parentProperty;
    }
    this.layer = parentProp;

    /**
     * The containing comp
     * @memberof DuAEProperty.prototype
     * @name comp
     * @readonly
     * @type {CompItem}
     */
    this.comp = this.layer.containingComp;

    // Recent versions of Ae have an id attribute, use it
    if (typeof property.id !== 'undefined') id = property.id;
    else {
        id += this.comp.id;
        id = parseInt(id);
    }
    this.id = id;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Recursively runs a method on all nested properties
 * @param {function} func The function to run. It must take a DuAEProperty object as its single argument
 */
DuAEProperty.prototype.do = function(func) {
    if (this.getProperty().propertyType == PropertyType.PROPERTY) {
        func(this);
    } else {
        for (var p = 1, num = this.numProperties(); p <= num; p++) {
            this.prop(p).do(func);
        }
    }
}

/**
 * Checks if this source property is a group of properties or a property
 * @return {Boolean} True if it's a group
 */
DuAEProperty.prototype.isGroup = function() {
    // Already checked
    if (typeof this.__isGroup !== 'undefined') return this.__isGroup;
    // Check
    var p = this.getProperty();
    if (p.propertyType == PropertyType.PROPERTY) {
        this.__isGroup = false;
    } else {
        this.__isGroup = true;
    }
    return this.__isGroup;
}

/**
 * Checks if this source property is a group of properties or a property
 * @return {Boolean} True if it's a property
 */
DuAEProperty.prototype.isProperty = function() {
    return !this.isGroup();
}

/**
 * Reimplements the <code>Property.isSpatial</code> attribute for convenience.
 * @return {Bool} true if the property is spatial.
 */
DuAEProperty.prototype.isSpatial = function() {
    if (this.isGroup()) return false;
    return this.getProperty().isSpatial;
}

/**
 * Reimplements the <code>Property.isSeparationLeader</code> attribute for convenience.
 * @return {Bool} true if the property is a separation leader.
 */
DuAEProperty.prototype.isSeparationLeader = function() {
    if (this.isGroup()) return false;
    return this.getProperty().isSeparationLeader;
}

/**
 * Reimplements the <code>Property.dimensionsSeparated</code> attribute for convenience.
 * @return {Bool} true if the property is a separation leader and has its dimensions seperated.
 */
DuAEProperty.prototype.dimensionsSeparated = function() {
    if (this.isGroup()) return false;
    if (!this.isSeparationLeader()) return false;
    return this.getProperty().isSeparationLeader;
}

/**
 * Reimplements the <code>Property.expression</code> attribute for convenience.
 * @return {string} the expression.
 */
DuAEProperty.prototype.expression = function() {
    if (this.isGroup()) return '';
    return this.getProperty().expression;
}

/**
 * Reimplements the <code>Property.value</code> attribute for convenience.
 * @param {Bool} [preExpression = false] Set to true to get the pre-expression value.
 * @return {*} A value appropriate for the type of the property (see Property.propertyValueType), or null if the property doesn't have a value (i.e. it's a group)
 */
DuAEProperty.prototype.value = function( preExpression ) {
    if (this.isGroup()) return null;
    preExpression = def(preExpression, false);

    var d = this.dimensions();
    var v;
    if (preExpression)
        v = this.getProperty().valueAtTime(this.comp.time, true);
    else
        v = this.getProperty().value;

    v = this.fixValue(v);

    return v;
}

/**
 * Reimplements the <code>Property.valueAtTime</code> mehtod for convenience.
 * @param {float} [time] If omitted, the current comp time.
 * @param {Bool} [preExpression = false] Set to true to get the pre-expression value.
 * @return {*} A value appropriate for the type of the property (see Property.propertyValueType), or null if the property doesn't have a value (i.e. it's a group)
 */
DuAEProperty.prototype.valueAtTime = function(time, preExpression) {
    if (this.isGroup()) return null;
    preExpression = def(preExpression, false);
    time = def(time, this.comp.time);

    var d = this.dimensions();
    var v = this.getProperty().valueAtTime(time, preExpression);
    v = this.fixValue(v);
    return v;
}

/**
 * Reimplements the <code>Property.selectedKeys</code> attribute for convenience.
 * @return {int[]} The list of selected keyframe indices.
 */
DuAEProperty.prototype.selectedKeys = function() {
    if (this.isGroup()) return [];
    var prop = this.getProperty();
    return prop.selectedKeys;
}

/**
 * Reimplements the <code>Property.keyTime</code> method for convenience.
 * @param {int} index The index of the keyframe.
 * @return {float} The time of the key.
 */
DuAEProperty.prototype.keyTime = function(index) {
    if (this.isGroup()) return 0;
    var prop = this.getProperty();
    return prop.keyTime(index);
}

/**
 * Reimplements the <code>Property.keyValue</code> method for convenience.
 * @param {int} index The index of the keyframe.
 * @return {*} The value of the key.
 */
DuAEProperty.prototype.keyValue = function(index) {
    if (this.isGroup()) return 0;
    var prop = this.getProperty();
    return prop.keyValue(index);
}

/**
 * Reimplements the <code>Property.keyValue</code> method for convenience.
 * @param {int} index The index of the keyframe.
 * @return {*} The value of the key.
 */
DuAEProperty.prototype.keyLabel = function(index) {
    // Doesn't exist in all versions
    if (jstype(prop.keyLabel) === 'undefined') return 0;
    if (this.isGroup()) return 0;
    var prop = this.getProperty();
    return prop.keyLabel(index);
}

/**
 * Reimplements the <code>Property.removeKey</code> method for convenience.
 * @param {int|DuAEKeyframe} key The index or the keyframe.
 */
DuAEProperty.prototype.removeKey = function(key) {
    if (this.isGroup()) return;
    if (key instanceof DuAEKeyframe) key = key._index;
    var prop = this.getProperty();
    prop.removeKey(key);
}

/**
 * Reimplements the <code>Property.nearestKeyIndex</code> method for convenience.
 * @param {float} t The time of the keyframe.
 */
DuAEProperty.prototype.nearestKeyIndex = function(t) {
    if (this.isGroup()) return 0;
    var prop = this.getProperty();
    return prop.nearestKeyIndex(t);
}

/**
 * Gets the unit of the property
 * @return {string} The unit
 */
DuAEProperty.prototype.unit = function() {
    if (typeof this.__unit !== 'undefined') return this.__unit;

    var prop = this.getProperty();
    this.__unitsText = prop.unitsText;
    this.__isPercent = this.__unitsText == localize("$$$/AE/TLW/GraphEditor/PercentSource=percent");
    this.__isAngle = this.__unitsText == localize("$$$/AE/TLW/GraphEditor/DegreesSource=degrees");
    this.__isPixels = this.__unitsText == localize("$$$/AE/TLW/GraphEditor/PixelsSource=pixels");
    this.__unit = '';
    if (this.__isPercent) this.__unit = "%";
    if (this.__isAngle) this.__unit = "°";
    if (this.__isPixels) this.__unit = localize("$$$/AE/TLW/GraphEditor/PixelsDest=px");
    return this.__unit;
}

/**
 * The units text of the property, same as DuAEProperty.getProperty().unitsText
 * @return {string} The unit
 */
DuAEProperty.prototype.unitsText = function() {
    this.unit();
    return this.__unitsText;
}

/**
 * Whether the value is a percent
 * @return {Boolean}
 */
DuAEProperty.prototype.isPercent = function() {
    this.unit();
    return this.__isPercent;
}

/**
 * Whether the value is an angle
 * @return {Boolean}
 */
DuAEProperty.prototype.isAngle = function() {
    this.unit();
    return this.__isAngle;
}

/**
 * Whether the value is a pixel value
 * @return {Boolean}
 */
DuAEProperty.prototype.isPixels = function() {
    this.unit();
    return this.__isPixels;
}

/**
 * Check if this is a dropdown property or effect.<br />
 * Note: On After Effects < 17.0.1 this always returns false.
 * @return {Boolean}
 */
DuAEProperty.prototype.isDropdown = function() {
    if(typeof this.__isDropdown !== 'undefined') return this.__isDropdown;
    
    if (this.isGroup()) {
        this.__isDropdown = false;
        return this.__isDropdown;
    }
    
    if (DuAE.version.atLeast('17.0.1')) {
        this.__isDropdown = this.getProperty().isDropdownEffect;
        return this.__isDropdown;   
    }

    this.__isDropdown = false;
    return this.__isDropdown;
}

/**
 * Gets the number of keyframes in the property
 * @param {Boolean} [recursive=true] If true and this is a group, returns the number of keyframes of all contained property
 * @return {int}
 */
DuAEProperty.prototype.numKeys = function (recursive) {
    recursive = def(recursive, true);
    if (!recursive && this.isGroup()) return 0;

    var prop = this.getProperty();

    var numKeys = 0;

    if (prop.propertyType == PropertyType.PROPERTY) {
        return prop.numKeys;
    }
    else {
        for (var p = 1, num = this.numProperties(); p <= num; p++) {
            numKeys += this.prop(p).numKeys();
        }
    }

    return numKeys;
}

/**
 * Reimplements the <code>PropertyGroup.numProperties</code> attribute.<br />
 * Use this to be sure to get the right number of props, in case some have been added or removed after the creation of the DuAEProperty object.
 * @return {int} The number of sub-properties.
 */
DuAEProperty.prototype.numProperties = function() {
    var prop = this.getProperty();
    if (prop.propertyType === PropertyType.PROPERTY) return 0;
    else return prop.numProperties;
}

/**
 * Reimplements the <code>PropertyGroup.property()</code> method for convenience.
 * @param {string|int} index Either the name, matchName or the index.
 * @return {DuAEProperty|null} The sub-property as DuAEProperty object or null if not found.
 */
DuAEProperty.prototype.prop = function(index) {
    var prop = this.getProperty();
    if (prop.propertyType === PropertyType.PROPERTY) return null;
    return new DuAEProperty(prop.property(index));
}

/**
 * Getter for the <code>PropertyBase.parentProperty</code> attribute for convenience.
 * @return {DuAEProperty|null} The parent property or null if not found.
 */
DuAEProperty.prototype.parentProperty = function() {
    return new DuAEProperty(
        this.getProperty().parentProperty
    );
}

/**
 * Reimplements the <code>Property.setPropertyParameters()</code> method.<br/>
 * <p>Works around issues caused by the AE API:<br/>
 * - The property object is invalidated (-> fix: use of DuAEProperty.getProperty)<br/>
 * - The effect loses its name (-> fix: name is reset afterwards)<br/>
 * - Names are not sanitized and may throw errors (-> fix: names are sanitized so no errors are thrown)<br/>
 * - Throws an error on AE < 17.0.1 (-> fix: just do nothing in this case)</p>
 * This method can be called either from the actual Property or its containing effect.
 * @param {string[]} names The list of names.
 */
DuAEProperty.prototype.setPropertyParameters = function( names ) {
    if (!DuAE.version.atLeast('17.0.1')) return;
    var p = null;
    if (this.isGroup()) p = this.prop(1);
    else p = this;
    if (!p) return;
    // Sanitize names
    for (var i = 0, n = names.length; i < n; i++) {
        var n = names[i];
        n = DuString.replace(n, '|', '/');
        n = DuString.trim(n);
        if (n == '') n = '-';
        // Must be unique
        names[i] = '***Duik Temp Name***';
        n = DuString.generateUnique(n, names);
        names[i] = n;
    }
    var effect = p.parentProperty();
    var name = effect.name;
    p.getProperty().setPropertyParameters(names);
    effect = p.parentProperty().getProperty();
    effect.name = name;
}

/**
 * Gets the original Property<br />
 * Always works even if this DuAEProperty represents an effect which has been broken<br />
 * ---AE Hack---
 * @memberof DuAEProperty
 * @return {PropertyBase} The property
 * @todo When returning an effect, check if the matchName corresponds too.
 */
DuAEProperty.prototype.getProperty = function() {
    // If the property is still valid, just return it
    if (Object.isValid(this.property)) return this.property;

    var parentProp = this.layer;
    for (var i = 0, n = this.parentIndices.length; i < n; i++) {
        parentProp = parentProp.property(this.parentIndices[i]);
    }
    this.property = parentProp;
    return this.property;
}

/**
 * Gets the number of dimensions of a property
 * @return {int}	The number of dimensions, 0 if this is not a dimensionnal value (ie color, text, shape...)
 */
DuAEProperty.prototype.dimensions = function() {
    // Already checked
    if (typeof this.__dimensions !== 'undefined') return this.__dimensions;

    var prop = this.getProperty();

    this.__dimensions = 0;
    if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL || prop.propertyValueType == PropertyValueType.ThreeD) {
        //if this is a position or scale and the layer is not 3D, AFX uses a 3D value in the position (with 0 as Z position), but the expression must return a 2D value.......
        if ((prop.matchName == "ADBE Scale" || prop.matchName == "ADBE Position") && !this.layer.threeDLayer) {
            this.__dimensions = 2;
        } else {
            this.__dimensions = 3;
        }
    } else if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL || prop.propertyValueType == PropertyValueType.TwoD) {
        this.__dimensions = 2;
    } else if (prop.propertyValueType == PropertyValueType.OneD) {
        this.__dimensions = 1;
    } else if (prop.propertyValueType == PropertyValueType.COLOR) {
        this.__dimensions = 4;
    }
    return this.__dimensions;
}

/**
 * Checks if this property value can be edited
 * @return {bool} true if the value of the property can be edited, false otherwise
 */
DuAEProperty.prototype.editable = function() {
    var prop = this.getProperty();

    if (prop.propertyType != PropertyType.PROPERTY) return false;
    if (prop.elided) return false;
    if (prop.dimensionsSeparated) return false;
    if (!prop.canVaryOverTime) return false;
    if (prop.propertyValueType == PropertyValueType.NO_VALUE) return false;
    if (prop.propertyValueType == PropertyValueType.CUSTOM_VALUE) return false;
    if (prop.propertyValueType == PropertyValueType.LAYER_INDEX) return false;
    if (prop.propertyValueType == PropertyValueType.MASK_INDEX) return false;
    try {
        if (typeof prop.value === 'undefined') return false;
    } catch (e) {
        return false;
    }

    if (this.isMasterProperty(prop)) return true;

    //TODO find a way to detect if prop is hidden without using a try/catch and without setting a value
    //try to set a value if there's no keyframe
    if (prop.numKeys == 0) {
        try {
            prop.setValueAtTime(0, prop.valueAtTime(0, true));
            prop.removeKey(1);
            return true;
        } catch (e) {
            return false;
        }
    } else {
        try {
            prop.setValueAtKey(1, prop.keyValue(1));
            return true;
        } catch (e) {
            return false;
        }
    }

    return true;
}

/**
 * Checks if a property is part of the master properties of a precomp
 * @return {Boolean} true if property is part of the master properties
 */
DuAEProperty.prototype.isMasterProperty = function() {
    var prop = this.getProperty();
    while (prop.parentProperty !== null) {
        if (prop.matchName == 'ADBE Layer Overrides') return true;
        prop = prop.parentProperty;
    }
    return false;
}

/**
 * Checks if this property value can be rigged (with an expression)
 * @return {bool} true if the value of the property can be rigged, false otherwise
 */
DuAEProperty.prototype.riggable = function() {
    var prop = this.getProperty();

    if (prop.propertyType != PropertyType.PROPERTY) return false;
    if (!prop.canVaryOverTime) return false;
    if (!prop.canSetExpression) return false;
    if (prop.elided) return false;
    if (prop.dimensionsSeparated) return false;
    if (typeof prop.expression !== 'string') return false;
    //TODO find a way to detect if prop is hidden without using a try/catch
    var expressionEnabled = prop.expressionEnabled;
    try {
        prop.expressionEnabled = expressionEnabled;
        return true;
    } catch (e) {
        return false;
    }
}

/**
 * Gets the key at a given index on a property
 * @param {int}	keyIndex The index of the key to retrieve. If the index is negative, it is counted from the end i.e. to retrieve the keyframe before the last one, use -2 (-1 is the last)
 * @return {DuAEKeyframe}	The keyframe, or null if incorrect index
 */
DuAEProperty.prototype.keyAtIndex = function(keyIndex) {
    var prop = this.getProperty();
    if (Math.abs(keyIndex) > prop.numKeys || keyIndex == 0) {
        return null;
    }
    if (keyIndex < 0) {
        keyIndex = prop.numKeys - keyIndex + 1;
    }

    var key = new DuAEKeyframe();
    key._time = prop.keyTime(keyIndex);
    if (prop.propertyValueType == PropertyValueType.TEXT_DOCUMENT) {
        key.value = prop.keyValue(keyIndex).text;
    } else {
        key.value = prop.keyValue(keyIndex);
    }
    key._inInterpolationType = prop.keyInInterpolationType(keyIndex);
    key._outInterpolationType = prop.keyOutInterpolationType(keyIndex);
    if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL || prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) {
        key._spatial = true;
        key.spatialProperties.inTangent = prop.keyInSpatialTangent(keyIndex);
        key.spatialProperties.outTangent = prop.keyOutSpatialTangent(keyIndex);
        key.spatialProperties._continuous = prop.keySpatialContinuous(keyIndex);
        key.spatialProperties._autoBezier = prop.keySpatialAutoBezier(keyIndex);
        key.spatialProperties._roving = prop.keyRoving(keyIndex);
    }
    key.inEase = prop.keyInTemporalEase(keyIndex);
    key.outEase = prop.keyOutTemporalEase(keyIndex);
    key._continuous = prop.keyTemporalContinuous(keyIndex);
    key._autoBezier = prop.keyTemporalAutoBezier(keyIndex);
    key._index = keyIndex;
    // Doesn't exist in all versions
    if (jstype(prop.keyLabel) === 'function') key.label = prop.keyLabel(keyIndex);

    return key;
}

/**
 * Gets the nearest key at a given time on a property
 * @param {float}	[time]	- The time of the key to retrieve. The current time by default.
 * @return {DuAEKeyframe|null}	The keyframe, or null if incorrect time or not found
 */
DuAEProperty.prototype.nearestKeyAtTime = function(time) {
    time = def(time, this.comp.time);
    var prop = this.getProperty();
    return this.keyAtIndex(prop.nearestKeyIndex(time));
}

/**
 * Gets the key at an exactly given time on a property
 * @param {float}	time	- The time of the key to retrieve.
 * @return {DuAEKeyframe}	The keyframe, or null if incorrect time
 */
DuAEProperty.prototype.keyAtTime = function(time) {
    var prop = this.getProperty();
    if (!prop.canVaryOverTime) return null;
    if (prop.numKeys == 0) return null;
    var key = this.keyAtIndex(prop.nearestKeyIndex(time));
    if (key === null) return key;
    if (DuMath.equals(key._time, time, 4)) return key;
    else return null;
}

/**
 * Gets the property keyframes in the whole timeline or in the time range<br />
 * The DuAEKeyframe._time will be adjusted relatively to the start of the time range instead of the startTime of the composition.
 * @param {boolean}	[selected=false]	- true to get only selected keyframes.
 * @param {float[]}	[timeRange]	- The time range, an array of two time values, in seconds. If not provided, will use the comp time range.<br />
 * Ignored if selected is true;
 * @return {DuAEKeyframe[]}	The keyframes, or null of this property is of type PropertyValueType.NO_VALUE or PropertyValueType.CUSTOM_VALUE
 */
DuAEProperty.prototype.keys = function(selected, timeRange) {
    var prop = this.getProperty();
    if (prop.propertyValueType == PropertyValueType.NO_VALUE) return [];
    if (prop.propertyValueType == PropertyValueType.CUSTOM_VALUE) return [];

    var comp = this.comp;
    if (timeRange == undefined) timeRange = [0, comp.duration];
    if (selected == undefined) selected = false;

    var keyFrames = [];

    if (prop.elided) return keyFrames;

    if (prop.isTimeVarying) {
        if (selected) {
            for (var keyIndex = 0; keyIndex < prop.selectedKeys.length; keyIndex++) {
                var key = this.keyAtIndex(prop.selectedKeys[keyIndex]);
                if (key._time >= timeRange[0] && key._time <= timeRange[1]) {
                    key._time = key._time - timeRange[0];
                    keyFrames.push(key);
                }
            }
        } else if (prop.numKeys > 0) {
            for (var keyIndex = 1; keyIndex <= prop.numKeys; keyIndex++) {
                var key = this.keyAtIndex(keyIndex);
                if (key._time >= timeRange[0] && key._time <= timeRange[1]) {
                    key._time = key._time - timeRange[0];
                    keyFrames.push(key);
                }
            }
        }
    }
    return keyFrames;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Recursilvely gets all animations in the property and subproperties in the whole timeline or in the time range<br />
 * The first DuAEKeyframe._time will be adjusted relatively to the start of the time range (if provided) instead of the startTime of the composition.
 * @param {boolean}	[selected=false]	- true to get only selected keyframes.
 * @param {float[]}	[timeRange]	- The time range, an array of two time values, in seconds. If not provided, will use the comp time range.
 * @return {DuAEPropertyGroupAnimation|DuAEPropertyAnimation}	The animations. A DuAEPropertyAnimation if prop is a Property, a PopertyGroupAnim if it is a PropertyGroup
 */
DuAEProperty.prototype.animation = function(selected, timeRange) {
    var comp = this.comp;
    timeRange = def(timeRange, [0, comp.duration]);
    selected = def(selected, false);

    var prop = this.getProperty();

    if (selected && !this.hasSelectedKeys()) return null;

    if (prop.propertyType === PropertyType.PROPERTY) {
        if (prop.propertyValueType == PropertyValueType.NO_VALUE) return null;
        if (prop.propertyValueType == PropertyValueType.CUSTOM_VALUE) return null;
        if (prop.elided) return null;
        if (!prop.canVaryOverTime) return null;
        var anim = new DuAEPropertyAnimation();
        anim._name = prop.name;
        anim._matchName = prop.matchName;
        if (prop.propertyValueType == PropertyValueType.TEXT_DOCUMENT) {
            anim.startValue = prop.valueAtTime(timeRange[0], true).text;
            anim.endValue = prop.valueAtTime(timeRange[1], true).text;
        } else {
            anim.startValue = prop.valueAtTime(timeRange[0], true);
            anim.endValue = prop.valueAtTime(timeRange[1], true);
        }

        anim.keys = this.keys(selected, timeRange);
        if (anim.keys.length > 0) {
            anim.startTime = anim.keys[0]._time;
            anim.endTime = anim.keys[anim.keys.length - 1]._time;
        } else {
            anim.startTime = 0;
            anim.endTime = 0;
        }
        anim.dimensions = this.dimensions();
        if (prop.canSetExpression) anim.expression = prop.expression;
        return anim;
    } else if (prop.numProperties > 0) {
        var groupAnim = new DuAEPropertyGroupAnimation();
        groupAnim._name = prop.name;
        groupAnim._matchName = prop.matchName;

        for (var propIndex = 1, numP = prop.numProperties; propIndex <= numP; propIndex++) {
            var subProp = new DuAEProperty(prop.property(propIndex));
            var anim = subProp.animation(selected, timeRange);
            if (anim != null) {
                if (groupAnim.startTime == null) groupAnim.startTime = anim.startTime;
                else if (groupAnim.startTime > anim.startTime) groupAnim.startTime = anim.startTime;
                if (groupAnim.endTime == null) groupAnim.endTime = anim.endTime;
                else if (groupAnim.endTime < anim.endTime) groupAnim.endTime = anim.endTime;
                groupAnim.anims.push(anim);
            }
        }
        return groupAnim;
    }
    return null;
}

// low-level undocumented method to get all expressions and cache them
DuAEProperty.prototype.addToExpressionCache = function() {
    this.do(function(prop) {
        var exp = new DuAEPropertyExpression(prop);
        if (!exp.empty) DuAEExpression.cache.push(exp);
    });
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Recursilvely gets the time of the first keyFrame in this prop or subprops
 * @param {Boolean} [selected=false] - true to check selected keyframes only
 * @return {float|null} The keyframe time or null if there are no keyframe
 */
DuAEProperty.prototype.firstKeyTime = function(selected) {
    selected = def(selected, false);
    var time = null;

    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY) {
        if (!prop.canVaryOverTime) return null;
        if (selected) {
            var keys = prop.selectedKeys;
            if (keys.length == 0) return null;
            for (var i = 0, n = keys.length; i < n; i++) {
                var t = prop.keyTime(keys[i]);
                if (time == null) time = t;
                else if (time > t) time = t;
            }
        } else {
            if (prop.numKeys == 0) return null;
            return prop.keyTime(1);
        }
    } else if (prop.numProperties > 0) {
        for (var i = 1, n = prop.numProperties; i <= n; i++) {
            var subProp = this.prop(i);
            var t = subProp.firstKeyTime(selected);
            if (time == null) time = t;
            else if (t != null) {
                if (time > t) time = t;
            }
        }
    }

    return time;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Recursilvely gets the time of the last keyFrame in this prop or subprops
 * @param {boolean} [selected=false] - true to check selected keyframes only
 * @return {float|null} The keyframe time or null if there are no keyframe
 */
DuAEProperty.prototype.lastKeyTime = function(selected) {
    selected = def(selected, false);
    var time = null;

    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY) {
        if (!prop.canVaryOverTime) return null;
        if (selected) {
            var keys = prop.selectedKeys;
            if (keys.length == 0) return null;
            for (var i = 0, n = keys.length; i < n; i++) {
                var t = prop.keyTime(keys[i]);
                if (time == null) time = t;
                else if (time < t) time = t;
            }
            return time;
        } else {
            if (prop.numKeys == 0) return null;
            return prop.keyTime(prop.numKeys);
        }
    } else if (prop.numProperties > 0) {
        for (var i = 1, n = prop.numProperties; i <= n; i++) {
            var subProp = this.prop(i);
            var t = subProp.lastKeyTime(selected);
            if (time == null) time = t;
            else if (t != null) {
                if (time < t) time = t;
            }
        }
    }

    return time;
}

/**
 * Sets a {@linkcode DuAEKeyframe} on a property
 * @param {DuAEKeyframe}	key	- The DuAEKeyframe.
 * @param {float}	[timeOffset=comp.time]	- The time offset (added to DuAEKeyframe._time) where to add the key frame.
 */
DuAEProperty.prototype.setKey = function(key, timeOffset) {
    var prop = this.getProperty();

    if (prop.elided) return;

    if (!prop.propertyType === PropertyType.PROPERTY) {
        DuDebug.throwError("Can not set a key on a group property", 'DuAEProperty.setKey');
        return;
    }
    if (!prop.canVaryOverTime) return;
    if (prop.isSeparationLeader)
        if (prop.dimensionsSeparated) return;

    timeOffset = def(timeOffset, this.comp.time);
    var time = key._time + timeOffset;
    var propDimensions = this.dimensions();
    var val = key.value;

    this.setValueAtTime(val, time);

    if (prop.numKeys == 0) return;

    //get the index of the created key
    var index = prop.nearestKeyIndex(time);

    //set label
    // Doesn't exist in all versions
    if (jstype(prop.setLabelAtKey) === 'function') prop.setLabelAtKey(index, key.label);

    //set interpolations
    if (key._spatial && (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL || prop.propertyValueType == PropertyValueType.TwoD_SPATIAL)) {
        try {
            prop.setSpatialContinuousAtKey(index, key.spatialProperties._continuous);
            prop.setSpatialAutoBezierAtKey(index, key.spatialProperties._autoBezier);
            prop.setRovingAtKey(index, key.spatialProperties._roving);
            prop.setSpatialTangentsAtKey(index, key.spatialProperties.inTangent, key.spatialProperties.outTangent);
        } catch (err) {
            if (DuESF.debug) alert(err.description);
        };
    }

    try {
        prop.setTemporalContinuousAtKey(index, key._continuous);
        prop.setTemporalAutoBezierAtKey(index, key._autoBezier);
        prop.setTemporalEaseAtKey(index, key.inEase, key.outEase);
        prop.setInterpolationTypeAtKey(index, key._inInterpolationType, key._outInterpolationType);
    } catch (err) {
        if (DuESF.debug) alert(err.description);
    }

}

/**
 * Checks if the property value is a number or an Array of Number.<br >
 * I.e if its value type is one of: one D, two D, three D (spatial or not), Color.
 * @return {bool}
 */
DuAEProperty.prototype.numerical = function() {
    var prop = this.getProperty();
    if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) return true;
    if (prop.propertyValueType == PropertyValueType.ThreeD) return true;
    if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) return true;
    if (prop.propertyValueType == PropertyValueType.TwoD) return true;
    if (prop.propertyValueType == PropertyValueType.OneD) return true;
    if (prop.propertyValueType == PropertyValueType.COLOR) return true;
    return false;
}

/**
 * Sets a value on a property, adjusting the dimensions if needed
 * @param {any} value - The value to set
 * @param {float} [defaultTime=comp().time] - The time at which to set the value if the property has keyframes
 * @return {boolean} True if the value has correctly been set, false otherwise.
 */
DuAEProperty.prototype.setValue = function(value, defaultTime) {
    var prop = this.getProperty();

    value = this.fixValue(value);

    if (prop.isSeparationLeader)
        if (prop.dimensionsSeparated) {
            var group = prop.parentProperty;
            var index = prop.propertyIndex;
            var i = 0;
            var ok = false;
            // The sepearation followers should be the next properties in the group
            while (true) {
                var p = group.property(index++);
                if (!p) { ok = false; break; }
                if (!p.isSeparationFollower) break;
                if (value.length == i) break;
                p = new DuAEProperty(p);
                var test = p.setValue( value [i] );
                if (i == 0) ok = test;
                else if (!test) ok = false;
                i++;
            }
            return ok;
        }

    if (!this.editable()) return false;

    if (prop.numKeys > 0) return this.setValueAtTime(value, defaultTime);

    try {
        prop.setValue(value);
        return true;
    } catch (e) {
        if (DuESF.debug) alert(e.description);
        return false;
    }
}

/**
 * Adds a new key
 * @param {KeyframeInterpolationType|string} [typeIn=KeyframeInterpolationType.LINEAR] - The in interpolation type (see AE API) or the string "roving" or "continuous"
 * @param {KeyframeInterpolationType|string} [typeOut=typeIn] - The out interpolation type (see AE API)
 * @param {float} [time] - If omitted, the current comp time
 * @param {int[]|int} [easeInValue=33] - The in interpolation ease value (used if typeIn is KeyframeInterpolationType.BEZIER)
 * @param {int[]|int} [easeOutValue=easeInValue] - The out interpolation ease value (used if typeOut is KeyframeInterpolationType.BEZIER)
 */
DuAEProperty.prototype.addKey = function( typeIn, typeOut, time, easeInValue, easeOutValue ) {
    if (this.isGroup()) return;

    typeIn = def(typeIn, KeyframeInterpolationType.LINEAR);
    typeOut = def(typeOut, typeIn);
    time = def(time, this.comp.time);

    easeInValue = def(easeInValue, 33);
    if (isNaN(easeInValue)) easeInValue = 33;
    easeOutValue = def(easeOutValue, easeInValue);
    if (isNaN(easeOutValue)) easeOutValue = 33;

    // Set value to create key
    this.setValueAtTime( this.valueAtTime(time, true), time);
    var prop = this.getProperty();
    var keyIndex = prop.nearestKeyIndex(time);
    this.setKeyInterpolation(keyIndex, typeIn, typeOut, easeInValue, easeOutValue);
}

/**
 * Sets a new keyframe on a property, adjusting the dimensions if needed, at desired time
 * @param {any} value - The value to set
 * @param {float} [time] - The time of the new keyframe
 * @return {boolean} True if the value has correctly been set, false otherwise.
 */
DuAEProperty.prototype.setValueAtTime = function(value, time) {
    var prop = this.getProperty();
    var dimensions = this.dimensions();

    time = def(time, this.comp.time);

    if (!prop.canVaryOverTime) return false;
    if (prop.isSeparationLeader)
        if (prop.dimensionsSeparated) return false;

    if (!this.editable()) return false;

    value = this.fixValue(value);

    try {
        prop.setValueAtTime(time, value);
        return true;
    } catch (e) {
        if (DuESF.debug) alert(e.description);
        return false;
    }
}

/**
 * Sets a new keyframe value, adjusting the dimensions if needed
 * @param {any} value - The value to set
 * @param {int} key - The index the keyframe
 * @return {boolean} True if the value has correctly been set, false otherwise.
 */
DuAEProperty.prototype.setValueAtKey = function(value, key) {
    var prop = this.getProperty();
    var dimensions = this.dimensions();

    if (!prop.canVaryOverTime) return false;
    if (prop.isSeparationLeader)
        if (prop.dimensionsSeparated) return false;

    if (!this.editable()) return false;

    if (key <= 0 || key > prop.numKeys) return false;

    value = this.fixValue(value);

    try {
        prop.setValueAtKey(key, value);
        return true;
    } catch (e) {
        if (DuESF.debug) alert(e.description);
        return false;
    }
}

/**
 * Sets the property animation on the property. This is a lower-level method than {@link DuAEProperty#setAnimation DuAEProperty.setAnimation()}.<br />
 * Use this method only to force the animation onto the property without checks.<br />
 * Must be used on a Property (not a group) with a DuAEPropertyAnimation (not a DuAEPropertyGroupAnimation).<br />
 * To easily set an animation on a property with automatic compatibility checks, you should use <code>setAnimation()</code>.
 * @param {DuAEPropertyAnimation} anim	- The animation
 * @param {float}	[time=comp.time]	- The time where to begin the animation
 * @param {boolean}	[setExpression=false]	- Sets the expression too
 * @param {boolean}	[replace=false]	- true to remove any existing keyframe on the properties before adding new keyframes
 * @param {boolean}	[offset=false]	- true to offset the current value, instead of replacing it
 * @return {boolean} true if the anim was actually set.
 */
DuAEProperty.prototype.setAnim = function(anim, time, setExpression, replace, offset) {
    var prop = this.getProperty();
    var comp = this.comp;

    time = def(time, comp.time);
    setExpression = def(setExpression, false);
    replace = def(replace, false);
    offset = def(offset, false);

    if (!this.numerical()) offset = false;

    var dimensions = anim.dimensions;

    var ok = false;

    if (anim == null) return true;
    if (anim.type == 'group') return false;

    if (this.editable()) {
        //keep current value
        var val = prop.valueAtTime(comp.time, true);

        //remove keyframes
        if (replace && prop.numKeys > 0) {
            for (var i = prop.numKeys; i > 0; i--) {
                prop.removeKey(i);
            }
            this.setValue(val);
        }

        //if there are keys, set them
        if (anim.keys.length > 0) {
            for (var iclef = 0; iclef < anim.keys.length; iclef++) {
                var key = anim.keys[iclef];
                if (offset) {
                    if (iclef == 0) key.value = val;
                    else key.value = val + (key.value - anim.startValue);
                }
                this.setKey(key, time);
                ok = true;
            }
        } else //set the start value
        {
            var value = anim.startValue;

            if (anim.startValue != null && !offset) {
                this.setValue(anim.startValue, time);
            }
            ok = true;
        }

        //set the expression
        if (this.riggable() && setExpression) {
            try {
                prop.expression = anim.expression;
            } catch (e) {
                if (DuESF.debug) alert(e.description);
            };
        }
    }

    return ok;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Sets all animations on a Property or a PropertyGroup.
 * @param {DuAEPropertyAnimation|DuAEPropertyGroupAnimation} anim The animation
 * @param {float}	[time=comp().time]	- The time where to begin the animation
 * @param {boolean}	[ignoreName=false]	- true to set the anim even if name of the property do not match the animation.
 * @param {boolean}	[setExpression=false]	- Sets the expression too
 * @param {boolean}	[onlyKeyframes=true]	- If false, the value of properties without keyframes will be set too.
 * @param {boolean}	[replace=false]	- true to remove any existing keyframe on the properties before adding new keyframes
 * @param {string[]}	[whiteList]	- A list of matchNames used as a white list for properties to set anims.<br />
 * Can be the matchName of a propertyGroup to set all the subproperties.<br />
 * Ignored if the list is empty.
 * @param {boolean}	[offset=false]	- true to offset the current value, instead of replacing it
 * @return {boolean} true if the anim was actually set.
 */
DuAEProperty.prototype.setAnimation = function(anim, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, set) {
    var prop = this.getProperty();

    time = def(time, this.comp.time);
    ignoreName = def(ignoreName, false);
    setExpression = def(setExpression, false);
    onlyKeyframes = def(onlyKeyframes, false);
    replace = def(replace, false);
    whiteList = def(whiteList, []);
    offset = def(offset, false);
    set = def(set, false);

    if (whiteList.length == 0) set = true;

    whiteList = new DuList(whiteList);
    if (whiteList.indexOf(anim._matchName) >= 0) set = true;

    var ok = false;

    if (anim == null) return true;

    if (anim.type == 'anim') {
        if (set) {
            var okToSet = false;
            if (prop.matchName == anim._matchName) {
                if (!ignoreName && prop.name == anim._name) okToSet = true;
                if (ignoreName) okToSet = true;
                if (onlyKeyframes && anim.keys.length == 0) okToSet = false;
            }

            if (okToSet) return this.setAnim(anim, time, setExpression, replace, offset);
        }
    } else {
        for (var i = 0; i < anim.anims.length; i++) {
            var propAnim = anim.anims[i];
            //find the property with the same name and matchname
            for (var j = 1, numProp = prop.numProperties; j <= numProp; j++) {
                var subProp = new DuAEProperty(prop.property(j));
                var okToSet = false;
                if (subProp.matchName == propAnim._matchName) {
                    if (!ignoreName && subProp.name == propAnim._name) okToSet = true;
                    if (ignoreName) okToSet = true;
                }
                if (okToSet) {
                    ok = subProp.setAnimation(propAnim, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, set);
                    break;
                }
            }
        }
    }

    return ok;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Removes the animation from the property
 * @param {Property|DuAEProperty} prop -The property
 * @param {boolean} [removeExpression=false] - Set to true to remove the expression too
 */
DuAEProperty.prototype.removeAnimation = function(prop, removeExpression) {
    removeExpression = def(removeExpression, false);
    this.do(function(prop) {
        prop = prop.getProperty();
        while (prop.numKeys > 0) {
            prop.removeKey(1);
        }
        if (removeExpression && prop.canSetExpression) {
            prop.expression = '';
        }
    });
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Selects the keyframes in the propoerty.<br />
 * Selects all nested keyframes if the property is a group.
 * @param {float} [inTime=0] - The time at which to select the keyframes
 * @param {float} [outTime=inTime] - The end time
 */
DuAEProperty.prototype.selectKeys = function(inTime, outTime) {
    // Defaults
    inTime = def(inTime, 0);
    outTime = def(outTime, inTime);
    // Run
    this.do(function(propInfo) {
        prop = propInfo.getProperty();
        if (prop.elided) return;
        if (prop.isSeparationLeader)
            if (prop.dimensionsSeparated) return;
        if (inTime == outTime) {
            //get key
            var key = propInfo.keyAtTime(inTime);
            if (key) prop.setSelectedAtKey(key._index, true);
        } else {
            //get keys
            var keys = propInfo.keys(false, [inTime, outTime]);
            if (!keys) return;
            for (var i = 0; i < keys.length; i++) {
                prop.setSelectedAtKey(keys[i]._index, true);
            }
        }
    });
}

/**
 * Gets an expression link to the property
 * @memberof DuAEProperty
 * @param {bool} [useThisComp=false] Whether to begin the expression by 'thisComp' or 'comp("name")'
 * @param {bool} [fromLayer=true] Whether to begin the expression by comp.layer or directly from the first prop of the layer
 * @return {str} The expression link to the property
 */
DuAEProperty.prototype.expressionLink = function(useThisComp, fromLayer) {
    var prop = this.getProperty();
    useThisComp = def(useThisComp, false);
    fromLayer = def(fromLayer, true);

    var exprCode = "";
    var name;
    while (prop.parentProperty !== null) {
        //do not translate master properties
        if (prop.parentProperty.matchName != "ADBE Layer Overrides") {
            if (prop.propertyType == PropertyType.PROPERTY) name = prop.propertyIndex;
            else if (prop.parentProperty.propertyType == PropertyType.INDEXED_GROUP) {
                name = "\"" + prop.name + "\"";
            } else {
                name = "\"" + prop.matchName + "\"";
            }
            compactName = DuAE.getCompactExpression(prop.matchName, name, prop);
            exprCode = compactName + exprCode;
        } else {
            exprCode = '("' + prop.name + '")';
        }

        // Traverse up the property tree
        prop = prop.parentProperty;
    }

    if (exprCode.indexOf("(") != 0 && exprCode != "") exprCode = '.' + exprCode;

    if (fromLayer) {
        var comp = prop.containingComp;
        // Prefix the layer reference
        name = "\"" + prop.name + "\"";
        exprCode = "layer(" + name + ")" + exprCode;
        // Prefix the comp reference
        if (useThisComp) exprCode = "thisComp." + exprCode;
        else exprCode = "comp(\"" + comp.name + "\")." + exprCode;
    }

    return exprCode;
}

/**
 * Sets interpolations on a keyframe.
 * @param {int} key - The key index
 * @param {KeyframeInterpolationType|string} typeIn - The in interpolation type (see AE API) or the string "roving" or "continuous"
 * @param {KeyframeInterpolationType|string} [typeOut=typeIn] - The out interpolation type (see AE API)
 * @param {int[]|int} [easeInValue=33] - The in interpolation ease value (used if typeIn is KeyframeInterpolationType.BEZIER)
 * @param {int[]|int} [easeOutValue=easeInValue] - The out interpolation ease value (used if typeOut is KeyframeInterpolationType.BEZIER)
 */
DuAEProperty.prototype.setKeyInterpolation = function(key, typeIn, typeOut, easeInValue, easeOutValue) {
    if (typeOut == undefined) typeOut = def(typeOut, typeIn);
    if (easeInValue == undefined) easeInValue = 33;
    if (isNaN(easeInValue)) easeInValue = 33;
    if (easeOutValue == undefined) easeOutValue = easeInValue;
    if (isNaN(easeOutValue)) easeOutValue = 33;

    easeInValue = new KeyframeEase(0, easeInValue);
    easeOutValue = new KeyframeEase(0, easeOutValue);

    var prop = this.getProperty();

    try {

        if (typeIn == "roving" && prop.isSpatial) {
            prop.setRovingAtKey(key, true);
        } else if (typeIn == "continuous") {
            prop.setInterpolationTypeAtKey(key, KeyframeInterpolationType.BEZIER);
            prop.setTemporalContinuousAtKey(key, true);
            prop.setTemporalAutoBezierAtKey(key, true);
            //not roving
            if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL || prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) prop.setRovingAtKey(key, false);
        } else if (typeIn != "roving") {
            // WARNING Influences HAVE TO be set BEFORE interpolation type
            // otherwise, the type gets back to Bézier.

            //influences
            var inInf = [easeInValue];
            var outInf = [easeOutValue];
            if (!prop.isSpatial) {
                var d = prop.value.length;
                while (d > inInf.length) {
                    inInf.push(easeInValue);
                    outInf.push(easeOutValue);
                }
            }
            prop.setTemporalEaseAtKey(key, inInf, outInf);
            
            //type
            prop.setInterpolationTypeAtKey(key, typeIn, typeOut);

            //not roving
            if (prop.isSpatial) {
                if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL || prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) prop.setRovingAtKey(key, false);
            }

            //not continuous
            prop.setTemporalContinuousAtKey(key, false);
        }
    } catch (e) {

    }
}

/**
 * Changes the ease influences of the selected keys
 * @param {PropertyBase[]|PropertyInfo[]} props - The properties
 * @param {int[]|int} [easeInValue] - The in interpolation ease value. Will be ignored if undefined.
 * @param {int[]|int} [easeOutValue] - The out interpolation ease value. Will be ignored if undefined.
 * @param {int[]|int} [velocityInValue] - The out interpolation ease value. Will be ignored if undefined.
 * @param {int[]|int} [velocityOutValue] - The out interpolation ease value. Will be ignored if undefined.
 * @param {boolean} [velocityAsPercent=false] - Use a percent instead of a value to set velocities.<br />
 * In this case, the proper velocity value will be deduced by multiplying the max speed of the property by the percent.
 */
DuAEProperty.prototype.setEase = function( easeInValue, easeOutValue, velocityInValue, velocityOutValue, velocityAsPercent ) {
    if (this.isGroup()) return;

    if (isNaN(easeInValue) && isdef( easeInValue )) easeInValue = 33;
    if (isNaN(easeOutValue) && isdef( easeOutValue )) easeOutValue = 33;
    if (isNaN(velocityInValue) && isdef( velocityInValue )) velocityInValue = 0;
    if (isNaN(velocityOutValue) && isdef( velocityOutValue )) velocityOutValue = 0;

    var comp = this.comp;
    var prop = this.getProperty();

    if (prop.canVaryOverTime) {

        var vInValue = velocityInValue;
        var vOutValue = velocityOutValue;

        for (var k = 0, numK = prop.selectedKeys.length; k < numK; k++) {
            var key = prop.selectedKeys[k];

            if (isdef( easeInValue )) {
                if (!isdef( velocityInValue ) && prop.keyInInterpolationType(key) == KeyframeInterpolationType.LINEAR) {
                    vInValue = 0;
                }
            }

            if (isdef( easeOutValue )) {
                if (!isdef( velocityOutValue ) && prop.keyOutInterpolationType(key) == KeyframeInterpolationType.LINEAR) {
                    vOutValue = 0;
                }
            }

            //compute the velocity
            if (velocityAsPercent) {
                //get speed just before and after as if it was linear
                var prevSpeed;
                var nextSpeed;
                var val = prop.keyValue(key);
                var currentTime = prop.keyTime(key);
                var valBefore = val;
                var valAfter = val;
                if (key > 1) {
                    var valBefore = prop.keyValue(key - 1);
                    var timeBefore = prop.keyTime(key - 1);
                    prevSpeed = DuMath.length(val, valBefore) / (currentTime - timeBefore);
                }

                if (key < prop.numKeys) {
                    var valAfter = prop.keyValue(key + 1);
                    var timeAfter = prop.keyTime(key + 1);
                    nextSpeed = DuMath.length(val, valAfter) / (timeAfter - currentTime);
                }

                if (!isdef( prevSpeed ) && !isdef( nextSpeed )) {
                    prevSpeed = 0;
                    nextSpeed = 0;
                } else if (!isdef( prevSpeed )) {
                    prevSpeed = nextSpeed;
                } else if (!isdef( nextSpeed )) {
                    nextSpeed = prevSpeed;
                }

                //detect sign
                var signBefore = 1;
                var signAfter = 1;
                if (this.dimensions() == 1) {
                    if (val < valBefore) signBefore = -1;
                    if (valAfter < val) signAfter = -1;
                }

                if (velocityInValue == velocityOutValue) {
                    //select average speed
                    var speed = (prevSpeed + nextSpeed) / 2;

                    vInValue = velocityInValue * speed / 100 * signBefore;
                    vOutValue = velocityOutValue * speed / 100 * signBefore;
                } else {

                    vInValue = velocityInValue * prevSpeed / 100 * signBefore;
                    vOutValue = velocityOutValue * nextSpeed / 100 * signAfter;
                }

            }

            //set interpolation 
            var easeIn = [new KeyframeEase(
                def(vInValue, prop.keyInTemporalEase(key)[0].speed),
                def(easeInValue, prop.keyInTemporalEase(key)[0].influence)
            )];
            var easeOut = [new KeyframeEase(
                def(vOutValue, prop.keyOutTemporalEase(key)[0].speed),
                def(easeOutValue, prop.keyOutTemporalEase(key)[0].influence)
            )];

            if (!prop.isSpatial) {
                for (var j = 1; j < prop.keyInTemporalEase(prop.selectedKeys[k]).length; j++) {
                    easeIn.push(new KeyframeEase(
                        def(vInValue, prop.keyInTemporalEase(key)[j].speed),
                        def(easeInValue, prop.keyInTemporalEase(key)[j].influence)
                    ));
                    easeOut.push(new KeyframeEase(
                        def(vOutValue, prop.keyOutTemporalEase(key)[j].speed),
                        def(easeOutValue, prop.keyOutTemporalEase(key)[j].influence)
                    ));
                }
            }

            //adjust interpolation types
            var inType = KeyframeInterpolationType.BEZIER;
            var outType = KeyframeInterpolationType.BEZIER;

            if (!isdef( easeInValue ) && !isdef( velocityInValue )) {
                inType = prop.keyInInterpolationType(key);
            }
            if (!isdef( easeOutValue ) && !isdef( velocityOutValue )) {
                outType = prop.keyOutInterpolationType(key);
            }

            prop.setInterpolationTypeAtKey(key, inType, outType);

            prop.setTemporalEaseAtKey(key, easeIn, easeOut);
        }
    }
}

/**
 * Sets the speed of a keyframe.
 * @param {int} key - The key index
 * @param {float} speed - The speed
 */
DuAEProperty.prototype.setKeySpeed = function(key, speed) {
    var prop = this.getProperty();
    // Get the current ease to reset the same tangents
    var inEase = prop.keyInTemporalEase(key);
    var outEase = prop.keyOutTemporalEase(key);

    if (!(speed instanceof Array)) speed = [speed];

    while (speed.length < inEase.length) speed.push(speed[0]);

    // Set the new speed
    for (var i = 0, n = inEase.length; i < n; i++) {
        inEase[i].speed = speed[i];
        outEase[i].speed = speed[i];
    }


    // Some properties can't set interpolation type
    try { prop.setTemporalEaseAtKey(key, inEase, outEase); }
    catch (e) {};
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Sets interpolations for all keyframes.
 * @param {KeyframeInterpolationType|string} typeIn - The in interpolation type (see AE API) or the string "roving" or "continuous"
 * @param {KeyframeInterpolationType|string} [typeOut=typeIn] - The out interpolation type (see AE API)
 * @param {int[]|int} [easeInValue=33] - The in interpolation ease value (used if typeIn is KeyframeInterpolationType.BEZIER)
 * @param {int[]|int} [easeOutValue=easeInValue] - The out interpolation ease value (used if typeOut is KeyframeInterpolationType.BEZIER)
 * @param {Bool} [selectedKeyframesOnly=false] - If true, only set the selected keyframes.
 */
DuAEProperty.prototype.setInterpolation = function(typeIn, typeOut, easeInValue, easeOutValue, selectedKeyframesOnly) {
    // Defaults
    selectedKeyframesOnly = def(selectedKeyframesOnly, false);
    typeOut = def(typeOut, typeIn);
    easeInValue = def(easeInValue, 33);
    if (isNaN(easeInValue)) easeInValue = 33;
    easeOutValue = def(easeOutValue, easeInValue);
    if (isNaN(easeOutValue)) easeOutValue = 33;

    // Run 
    this.do(function(propInfo) {
        var prop = propInfo.getProperty();
        if (selectedKeyframesOnly) {
            for (var i = 0; i < prop.selectedKeys.length; i++) {
                propInfo.setKeyInterpolation(prop.selectedKeys[i], typeIn, typeOut, easeInValue, easeOutValue);
            }
        }
        else {
            for (var i = 1, n = prop.numKeys; i <= n; i++) {
                propInfo.setKeyInterpolation(i, typeIn, typeOut, easeInValue, easeOutValue);
            }
        }
    });
}

/**
 * Computes a percentage from a velocity on a given keyframe. 
 * @param {int} keyIndex - The index of the keyframe where to compute the velocity
 * @return {float[]} The velocities [in, out] as a percentage.
 */
DuAEProperty.prototype.velocityToPercent = function(key) {
    var prop = this.getProperty();

    var speedIn = prop.keyInTemporalEase(key)[0].speed;
    var speedOut = prop.keyOutTemporalEase(key)[0].speed;

    //get speed just before and after as if it was linear
    var prevSpeed = 0;
    var nextSpeed = 0;
    var val = prop.keyValue(key);
    var currentTime = prop.keyTime(key);
    if (key > 1) {
        var valBefore = prop.keyValue(key - 1);
        var timeBefore = prop.keyTime(key - 1);
        prevSpeed = DuMath.length(val, valBefore) / (currentTime - timeBefore);
        if (this.dimensions() == 1) {
            if (val - valBefore < 0 && speedIn < 0) prevSpeed = -prevSpeed;
        }
    }

    if (key < prop.numKeys) {
        var valAfter = prop.keyValue(key + 1);
        var timeAfter = prop.keyTime(key + 1);
        nextSpeed = DuMath.length(val, valAfter) / (timeAfter - currentTime);
        // Sign
        if (this.dimensions() == 1) {
            if (valAfter - val < 0 && speedOut < 0) nextSpeed = -nextSpeed;
        }
    }

    //get average speed
    var speed = (prevSpeed + nextSpeed) / 2;

    //compare to the original speeds 
    var speedInAsPercent = speedIn / speed * 100;
    var speedOutAsPercent = speedOut / speed * 100;

    return [speedInAsPercent, speedOutAsPercent];
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Checks if the property has some selected keyframes.<br />
 * The property can be either a Property or a PropertyGroup.
 * @return {boolean} true if the property have at least one selected keyframe
 */
DuAEProperty.prototype.hasSelectedKeys = function() {
    var yes = false;

    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY) {
        if (prop.selectedKeys.length > 0) {
            yes = true;
        }
    } else if (prop.numProperties > 0) {
        for (var propIndex = 1, numP = this.numProperties(); propIndex <= numP; propIndex++) {
            yes = this.prop(propIndex).hasSelectedKeys();
            if (yes) break;
        }
    }
    return yes;
}

/**
 * Sets the spatial interpolation of the keyframes on the property
 * @param {KeyframeInterpolationType} typeIn - The in interpolation type (see AE API)
 * @param {KeyframeInterpolationType} [typeOut=typeIn] - The in interpolation type (see AE API)
 * @param {Bool} [selectedKeyframesOnly=false] - If true, only set the selected keyframes.
 */
DuAEProperty.prototype.setSpatialInterpolation = function(typeIn, typeOut, selectedKeyframesOnly) {
    var prop = this.getProperty();
    typeOut = def(typeOut, typeIn);
    selectedKeyframesOnly = def(selectedKeyframesOnly, false);

    if (!prop.isSpatial) return;
    if (prop.selectedKeys.length == 0) return;

    var keys = [];
    if (selectedKeyframesOnly) keys = prop.selectedKeys;
    else for (var k = 1, n = prop.numKeys; k <= n; k++) keys.push(k);

    for (var k = 0, numK = keys.length; k < numK; k++) {
        this.setSpatialInterpolationAtKey(keys[k], typeIn, typeOut);
    }
}

/**
 * Sets the spatial interpolation of the keyframes
 * @param {int|DuAEKeyframe} key - The keyframe or its index
 * @param {KeyframeInterpolationType} typeIn - The in interpolation type (see AE API)
 * @param {KeyframeInterpolationType} [typeOut=typeIn] - The in interpolation type (see AE API)
 */
DuAEProperty.prototype.setSpatialInterpolationAtKey = function (key, typeIn, typeOut) {
    var prop = this.getProperty();

    typeOut = def(typeOut, typeIn);
    var k = key;
    if (key instanceof DuAEKeyframe) k = key._index;

    if (typeIn == KeyframeInterpolationType.BEZIER && typeOut == KeyframeInterpolationType.BEZIER) {
        prop.setSpatialAutoBezierAtKey(k, true);
    } else if (typeIn == KeyframeInterpolationType.LINEAR && typeOut == KeyframeInterpolationType.LINEAR) {
        prop.setSpatialContinuousAtKey(k, false);
        prop.setSpatialAutoBezierAtKey(k, false);
        if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) {
            prop.setSpatialTangentsAtKey(k, [0, 0, 0], [0, 0, 0]);
        } else if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) {
            prop.setSpatialTangentsAtKey(k, [0, 0], [0, 0]);
        }
    } else if (typeIn == KeyframeInterpolationType.BEZIER) {
        prop.setSpatialContinuousAtKey(k, false);
        prop.setSpatialAutoBezierAtKey(k, false);
        if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) {
            if (prop.keyInSpatialTangent(k)[0] == 0 && prop.keyInSpatialTangent(k)[1] == 0 && prop.keyInSpatialTangent(k)[2] == 0) {
                prop.setSpatialAutoBezierAtKey(k, true);
            }
            prop.setSpatialTangentsAtKey(k, prop.keyInSpatialTangent(k), [0, 0, 0]);
        } else if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) {
            if (prop.keyInSpatialTangent(k)[0] == 0 && prop.keyInSpatialTangent(k)[1] == 0) {
                prop.setSpatialAutoBezierAtKey(k, true);
            }
            prop.setSpatialTangentsAtKey(k, prop.keyInSpatialTangent(k), [0, 0]);
        }
    } else if (typeIn == KeyframeInterpolationType.LINEAR) {
        prop.setSpatialContinuousAtKey(k, false);
        prop.setSpatialAutoBezierAtKey(k, false);
        if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) {
            if (prop.keyOutSpatialTangent(k)[0] == 0 && prop.keyOutSpatialTangent(k)[1] == 0 && prop.keyOutSpatialTangent(k)[2] == 0) {
                prop.setSpatialAutoBezierAtKey(k, true);
            }
            prop.setSpatialTangentsAtKey(k, [0, 0, 0], prop.keyOutSpatialTangent(k));
        } else if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) {
            if (prop.keyOutSpatialTangent(k)[0] == 0 && prop.keyOutSpatialTangent(k)[1] == 0) {
                prop.setSpatialAutoBezierAtKey(k, true);
            }
            prop.setSpatialTangentsAtKey(k, [0, 0], prop.keyOutSpatialTangent(k));
        }
    }
}

/**
 * Fixes the spatial interpolation of the selected keys.<br />
 * Sets the interpolation to linear when the property does not move between keyframes
 * @param {int} [precision=1] - The precision for float number comparison, number of decimals. Set to -1 to not use.
 * @param {Bool} [selectedKeyframesOnly=false] - If true, only set the selected keyframes.
 */
DuAEProperty.prototype.fixSpatialInterpolation = function( precision, selectedKeyframesOnly ) {
    precision = def(precision, 1);
    var prop = this.getProperty();

    if (!prop.isSpatial) return;
    if (!prop.canVaryOverTime) return;

    selectedKeyframesOnly = def(selectedKeyframesOnly, false);

    var keys = [];
    if (selectedKeyframesOnly) keys = prop.selectedKeys;
    else for (var k = 1, n = prop.numKeys; k <= n; k++) keys.push(k);

    for (var i = 0, numK = keys.length; i < numK; i++) {
        var k = keys[i];
        if (k == prop.numKeys) continue;
        //get this key value
        var keyValue = prop.valueAtTime(prop.keyTime(k), true);
        //get next key value
        var nextKeyValue = prop.valueAtTime(prop.keyTime(k + 1), true);

        //compare and set
        if (DuMath.equals( keyValue, nextKeyValue, precision )) {
            if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) {
                prop.setSpatialTangentsAtKey(k, prop.keyInSpatialTangent(k), [0, 0, 0]);
                prop.setSpatialTangentsAtKey(k+1, [0, 0, 0], prop.keyOutSpatialTangent(k+1));
            }
            else if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) {
                prop.setSpatialTangentsAtKey(k, prop.keyInSpatialTangent(k), [0, 0]);
                prop.setSpatialTangentsAtKey(k+1, [0, 0], prop.keyOutSpatialTangent(k+1));
            }
        }
    }
}

/**
 * Reimplements the <code>Property.propertyValueType</code> attribute.
 * @return {PropertyValueType|null} The value type, or null if this is a group
 */
DuAEProperty.prototype.propertyValueType = function() {
    if (this.isGroup()) return null;
    var prop = this.getProperty();
    return prop.PropertyValueType;
}

/**
 * Reimplements the <code>Property.keyInSpatialTangent</code> method.
 * @param {int|DuAEKeyframe} key The keyframe or its index.
 * @return {float[]} The tangent
 */
DuAEProperty.prototype.keyInSpatialTangent = function(key) {
    if (this.isGroup()) return [];
    if (!this.isSpatial()) return [];
    var prop = this.getProperty();
    if (key instanceof DuAEKeyframe) key = key._index;
    return prop.keyInSpatialTangent(key);
}

/**
 * Reimplements the <code>Property.keyOutSpatialTangent</code> method.
 * @param {int|DuAEKeyframe} key The keyframe or its index.
 * @return {float[]} The tangent
 */
DuAEProperty.prototype.keyOutSpatialTangent = function(key) {
    if (this.isGroup()) return [];
    if (!this.isSpatial()) return [];
    var prop = this.getProperty();
    if (key instanceof DuAEKeyframe) key = key._index;
    return prop.keyOutSpatialTangent(key);
}

/**
 * Reimplements the <code>Property.setSpatialTangentsAtKey</code> method.
* @param {int|DuAEKeyframe} key The keyframe or its index.
* @param {float[]} inTangent The in tangent.
* @param {float[]} outTangent The out tangent.
 */
DuAEProperty.prototype.setSpatialTangentsAtKey = function(key, inTangent, outTangent) {
    if (this.isGroup()) return;
    if (!this.isSpatial()) return;
    if (this.dimensionsSeparated()) return;
    var prop = this.getProperty();
    // Adjust dimensions
    if (prop.propertyValueType == PropertyValueType.ThreeD_SPATIAL) {
        while (inTangent.length > 3) inTangent.pop();
        while (inTangent.length < 3) inTangent.push(0);
        while (outTangent.length > 3) outTangent.pop();
        while (outTangent.length < 3) outTangent.push(0);
    }
    else if (prop.propertyValueType == PropertyValueType.TwoD_SPATIAL) {
        while (inTangent.length > 2) inTangent.pop();
        while (inTangent.length < 2) inTangent.push(0);
        while (outTangent.length > 2) outTangent.pop();
        while (outTangent.length < 2) outTangent.push(0);
    }
    else return;
    if (key instanceof DuAEKeyframe) key = key._index;

    prop.setSpatialTangentsAtKey(key, inTangent, outTangent);
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Removes all unneeded keyframes from the property.< br/>
 * Also checks the interpolation values to reset the correct display as linear/smooth.
 * @param {int} [precision=1] - The precision for float number comparison, number of decimals. Set to -1 to not use.
 */
DuAEProperty.prototype.cleanKeyframes = function( precision ) {
    precision = def(precision, 1);
    
    var prop = this.getProperty();

    var frameDuration = this.comp.frameDuration;

    if (prop.propertyType == PropertyType.PROPERTY) {
        var valueType = prop.propertyValueType;
        var numKeys = prop.numKeys;
        if (numKeys == 0) return;
        if (numKeys == 1) {
            prop.removeKey(1);
            return;
        }

        for (var i = numKeys; i >= 1; i--) {

            var t = prop.keyTime(i);
            var cV = prop.keyValue(i);
            var pV = prop.valueAtTime(t - frameDuration, true);
            if (valueType == PropertyValueType.SHAPE) {
                if (!DuAEProperty.shapeValueEquals(cV, pV, precision)) continue;
            }
            else if (valueType == PropertyValueType.TEXT_DOCUMENT) {
                if (!DuAEProperty.textValueEquals(cV, pV)) continue;
            }
            else if (!DuMath.equals(pV, cV, precision)) continue;

            var nV = prop.valueAtTime(t + frameDuration, true);

            if (valueType == PropertyValueType.SHAPE) {
                if (!DuAEProperty.shapeValueEquals(cV, nV, precision)) continue;
            }
            else if (valueType == PropertyValueType.TEXT_DOCUMENT) {
                if (!DuAEProperty.textValueEquals(cV, nV)) continue;
            }
            else if (!DuMath.equals(nV, cV, precision)) continue;

            prop.removeKey(i);

        }
    } else {
        for (var p = 1, num = this.numProperties(); p <= num; p++) {
            this.prop(p).cleanKeyframes();
        }
    }
}

/**
 * Gets the speed of a property at a given time, in unit per second (and not per frame as speeds in the After Effects API)
 * @param {float} [time=comp().time] - The time.
 * @param {boolean} [preExpression=true] - true to get the pre-expression speed.