Source

DuAEF_DuGR_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: Language is set to: " + i18n.getLocale());

    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, msg)
{
	msg = def(msg, "");
	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';
	if (msg == "") errorString += error.description;
	else errorString += msg;
	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;
	}
}
DuESF.initMethods.push(DuColor.init);

/**
	* 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(namespace + "/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(namespace + "/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(namespace + "/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 = {"pd": "Ch'ti / Picard", "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= {"Ch'ti / Picard": "pd", "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'])
      {
        DuDebug.log('gettext: Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');
        return;
      }

      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 = new File( DuPath.join([_folder, _prefix + locale + _suffix]) );
        if (!jsonFile.exists)
        {
          DuDebug.log("gettext: Can't find translation file: " + jsonFile.fsName)
          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;
})();
// ==================== |--------| ====================
// ==================== | base64 | ====================
// ==================== |--------| ====================

/**
 * Base64 codec.
 * @name Base64
 * @namespace
 * @see {@link https://github.com/dankogai/js-base64}
 * @license BSD-3-Clause http://opensource.org/licenses/BSD-3-Clause
 * @category DuESF
 * @subcategory Third Party
 * @version 2.6.4
 * @copyright 2014, Dan Kogai All rights reserved.
 */


var Base64 = (function() {
    var version = "2.6.4";
    // constants
    var b64chars
        = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    var b64tab = function(bin) {
        var t = {};
        for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i;
        return t;
    }(b64chars);
    var fromCharCode = String.fromCharCode;
    // encoder stuff
    var cb_utob = function(c) {
        if (c.length < 2) {
            var cc = c.charCodeAt(0);
            return cc < 0x80 ? c
                : cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6))
                                + fromCharCode(0x80 | (cc & 0x3f)))
                : (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f))
                    + fromCharCode(0x80 | ((cc >>>  6) & 0x3f))
                    + fromCharCode(0x80 | ( cc         & 0x3f)));
        } else {
            var cc = 0x10000
                + (c.charCodeAt(0) - 0xD800) * 0x400
                + (c.charCodeAt(1) - 0xDC00);
            return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07))
                    + fromCharCode(0x80 | ((cc >>> 12) & 0x3f))
                    + fromCharCode(0x80 | ((cc >>>  6) & 0x3f))
                    + fromCharCode(0x80 | ( cc         & 0x3f)));
        }
    };
    var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
    var utob = function(u) {
        return u.replace(re_utob, cb_utob);
    };
    var cb_encode = function(ccc) {
        var padlen = [0, 2, 1][ccc.length % 3],
        ord = ccc.charCodeAt(0) << 16
            | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8)
            | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)),
        chars = [
            b64chars.charAt( ord >>> 18),
            b64chars.charAt((ord >>> 12) & 63),
            padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63),
            padlen >= 1 ? '=' : b64chars.charAt(ord & 63)
        ];
        return chars.join('');
    };
    var btoa = function(b) {
        if (b.match(/[^\x00-\xFF]/)) throw new RangeError(
            'The string contains invalid characters.'
        );
        return b.replace(/[\s\S]{1,3}/g, cb_encode);
    };
    var _encode = function(u) {
        return btoa(utob(String(u)));
    };
    var encode = function(u) {
        return _encode(u);
    };
    var encodeURI = function(u) { return encode(u, true) };
    // decoder stuff
    var re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
    var cb_btou = function(cccc) {
        switch(cccc.length) {
        case 4:
            var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
                |    ((0x3f & cccc.charCodeAt(1)) << 12)
                |    ((0x3f & cccc.charCodeAt(2)) <<  6)
                |     (0x3f & cccc.charCodeAt(3)),
            offset = cp - 0x10000;
            return (fromCharCode((offset  >>> 10) + 0xD800)
                    + fromCharCode((offset & 0x3FF) + 0xDC00));
        case 3:
            return fromCharCode(
                ((0x0f & cccc.charCodeAt(0)) << 12)
                    | ((0x3f & cccc.charCodeAt(1)) << 6)
                    |  (0x3f & cccc.charCodeAt(2))
            );
        default:
            return  fromCharCode(
                ((0x1f & cccc.charCodeAt(0)) << 6)
                    |  (0x3f & cccc.charCodeAt(1))
            );
        }
    };
    var btou = function(b) {
        return b.replace(re_btou, cb_btou);
    };
    var cb_decode = function(cccc) {
        var len = cccc.length,
        padlen = len % 4,
        n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0)
            | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0)
            | (len > 2 ? b64tab[cccc.charAt(2)] <<  6 : 0)
            | (len > 3 ? b64tab[cccc.charAt(3)]       : 0),
        chars = [
            fromCharCode( n >>> 16),
            fromCharCode((n >>>  8) & 0xff),
            fromCharCode( n         & 0xff)
        ];
        chars.length -= [0, 0, 2, 1][padlen];
        return chars.join('');
    };
    var _atob = function(a){
        return a.replace(/\S{1,4}/g, cb_decode);
    };
    var atob = function(a) {
        return _atob(String(a).replace(/[^A-Za-z0-9\+\/]/g, ''));
    };
    var _decode = function(a) { return btou(_atob(a)) };
    var _fromURI = function(a) {
        return String(a).replace(/[-_]/g, function(m0) {
            return m0 == '-' ? '+' : '/'
        }).replace(/[^A-Za-z0-9\+\/]/g, '');
    };
    var decode = function(a){
        return _decode(_fromURI(a));
    };

    // export Base64
    return {
        VERSION: version,
        atob: atob,
        btoa: btoa,
        fromBase64: decode,
        toBase64: encode,
        utob: utob,
        encode: encode,
        encodeURI: encodeURI,
        btou: btou,
        decode: decode
    };
})();
// ==================== |---------| ====================
// ==================== | 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 = {};

/**
 * The list of legit characters for base64 encoding
 * @type {string[]}
 */
DuFile.base64Chars = [
	"A",
	"B",
	"C",
	"D",
	"E",
	"F",
	"G",
	"H",
	"I",
	"J",
	"K",
	"L",
	"M",
	"N",
	"O",
	"P",
	"Q",
	"R",
	"S",
	"T",
	"U",
	"V",
	"W",
	"X",
	"Y",
	"Z",
	"a",
	"b",
	"c",
	"d",
	"e",
	"f",
	"g",
	"h",
	"i",
	"j",
	"k",
	"l",
	"m",
	"n",
	"o",
	"p",
	"q",
	"r",
	"s",
	"t",
	"u",
	"v",
	"w",
	"x",
	"y",
	"z",
	"0",
	"1",
	"2",
	"3",
	"4",
	"5",
	"6",
	"7",
	"8",
	"9",
	"+",
	"/",
];

/**
	* 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)
{
	if (!(file instanceof File)) file = new File(file);
	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;
}

/**
 * Encodes a file to a base64 string.
 * @param {string|File} file The file or its path
 * @return {string} The base64 string
 */
DuFile.toBase64 = function( file )
{
	if (!(file instanceof File)) file = new File(file);
    if (!file.exists) return "";

    file.encoding = "binary";
    file.open("r");
    var bin = file.read();
    file.close();

    var ecdStr = Base64.btoa(bin);

    return ecdStr;
}

/**
 * Checks if the base64 string seems valid
 * @param {string} b64 The string to check
 * @return {bool}
 */
DuFile.checkBase64 = function ( b64 )
{
	var len = b64.length

    if (len % 4 > 0) {
        return false;
    }

    // If there's a dot, that's probably a file name
    if ( b64.indexOf(".") >= 0 )
        return false;

    return true;
}

/**
 * Writes a file from a base64 string.
 * @param {string} b64 The base64 string
 * @param {string|File} file The destination file or its path
 * @return {File|null} The File object or null if it fails
 */
DuFile.fromBase64 = function( b64, file )
{
	if (b64 == "") return null;
    if (!DuFile.checkBase64(b64)) return null;

    // Convert to bin
    var bin = Base64.atob(b64);

    // Write to file
    if (!(file instanceof File)) file = new File( file );
    file.encoding = 'BINARY';

    try
	{
        if (!file.open('w')) return null;
        file.write(bin);
		file.close();
		return file;
    }
    catch (e) { return null; }
}

/**
	* 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;*/

    // Add a progress bar
    panel.progressBar = new DuProgressBar("", bottomUberGroup);

    // 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;
}

/**
 * Finds the window containing this ScriptUI Control
 * @param {Control} scriptUIControl The ScriptUI Control
 * @return {Window} The containing ScriptUI Window
 */
DuScriptUI.parentWindow = function ( scriptUIControl )
{
    var p = scriptUIControl;
    while(p.parent) p = p.parent;
    return p;
}// ==================== |--------| ====================
// ==================== | 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 (currentLocale != '' || 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, true );

        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.setCurrentData = function( data )
    {
        selector.setCurrentIndex(0);
        for (var i = 0, n = selector.items.length; i < n; i++)
        {
            if (selector.items[i][3] == data)
            {
                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} [filters] - The file type filters.
 * @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="Magic is happening"] The title of the progress bar
    * @param {Group} [container] A ScriptUI Group to add the progress bar. If not provided, the bar will be added in a new window popup
 * @category DuScriptUI
 */
DuProgressBar = function( title, container )
{
    title = def(title, i18n._("Magic is happening"));
    if (title == "") title = i18n._("Magic is happening");
    this.container = def(container, null );
    this.isPopup = !this.container;
    if (!this.isPopup) this.window = DuScriptUI.parentWindow(this.container);
    else this.window = null;

    this.built = false;

    // Create ui
    this.__setupUI = function()
    {
        if (this.built) return;

        if (this.isPopup)
        {
            this.window = new Window('palette', '', [0,0,360,86]);
            this.container = this.window;
        }

        this.rootGroup = this.container.add('group');
        this.rootGroup.margins = 0;
        this.rootGroup.orientation = 'column';

        this.stage = this.rootGroup.add( 'statictext', this.isPopup ? { x:20, y:15, width:320, height:16 } : undefined, i18n._("Working"));

        this.barGroup = this.rootGroup.add('group', this.isPopup ? { x:20, y:37, width:320, height:12 } : undefined);
        this.barGroup.orientation = 'stacked';
        this.barGroup.margins = 0;
        this.bar = this.barGroup.add( 'progressbar', this.isPopup ? { x:0, y:0, width:320, height:12 } : undefined, 0, 0);
        this.image = this.barGroup.add( 'image', this.isPopup ? { x:0, y:0, width:320, height:12 } : undefined, DuScriptUI.Icon.PROGRESS.binAsString );
        
        this.label = this.rootGroup.add( 'statictext', this.isPopup ? { x:20, y:51, width:320, height:16 } : undefined);

        this.message =  title + ' %1';

        if (this.isPopup) this.window.center();

        DuScriptUI.layout( this.container );

        this.built = true;
    }

    // Destroy ui
    this.__delete = function()
    {
        if (!this.built) return;
        // Remove controls
        this.rootGroup.remove( this.label );
        this.barGroup.remove( this.bar );
        this.barGroup.remove( this.image );
        this.rootGroup.remove( this.barGroup );
        this.rootGroup.remove( this.stage );
        this.container.remove( this.rootGroup );
        // Delete to (try to) free memory
        delete this.label;
        delete this.image;
        delete this.bar;
        delete this.barGroup;
        delete this.stage;
        delete this.rootGroup;
        if (this.isPopup)
        {
            delete this.container;
            delete this.window;
        }
        this.built = false;

        // Garbage collection
        $.gc();
    }

    // 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;
        if (this.window.update) this.window.update();
    };
}

/**
 * Changes the text of the label
 * @param {string} message The text
 */
DuProgressBar.prototype.msg = function( message )
{
    this.__setupUI();
    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.__setupUI();
    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.__setupUI();
    this.rootGroup.show();
    if (this.isPopup) this.window.show();
    this.msg(message);  
}

/**
 * Hides and resets the progress bar to 0 and default texts
 */
DuProgressBar.prototype.reset = function(  )
{
    this.hide();
    this.close();
    this.__delete();
}

/**
 * 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 )
{
    this.__setupUI();
    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 )
{
    this.__setupUI();
    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 )
{
    this.__setupUI();
    maxValue = def(maxValue, 1);
    this.setMax(this.bar.maxvalue + maxValue, false);
}

/**
 * Hides the progress bar
 */
DuProgressBar.prototype.hide = function( )
{
    if (!this.built) return;
    this.rootGroup.hide();
    this.__delete();
    if (!this.isPopup) DuScriptUI.layout( this.container );
}

/**
 * Closes the progress bar
 */
DuProgressBar.prototype.close = function( )
{
    if (!this.built) return;
    if (this.isPopup) this.window.close();
    this.hide();
}

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 () {
        // Only if visible
        if (!duLibrary.visible) return;
        //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, options.refreshInterval);

    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 (Open Cut Out) Documents and armatures
 * @namespace
 * @category OCO
 */
var OCO = {};

/**
 * Types of OCO armatures
 * @enum {string}
 */
OCO.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}
 */
OCO.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}
 */
OCO.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}
 */
OCO.LimbType = {
    HOMINOID: 'hominoid',
    PLANTIGRADE: 'plantigrade',
    DIGITIGRADE: 'digitigrade',
    UNGULATE: 'ungulate',
    ARTHROPOD: 'arthropod',
    CUSTOM: 'custom'
}

/**
 * Sides for the limbs Use these with {@link OCO.Location} to differenciate similar limbs,<br />
 * for example, a leg can be Front-Right, Front-Left, Back-Right, Back-Left, etc.
 * @enum {string}
 */
OCO.Side = {
    LEFT: "L",
    RIGHT: "R",
    NONE: ''
}

/**
 * Locations for the limbs. Use these with {@link OCO.Side} to differenciate similar limbs,<br />
 * for example, a leg can be Front-Right, Front-Left, Back-Right, Back-Left, etc.
 * @enum {string}
 */
OCO.Location = {
    FRONT: "Fr",
    BACK: "Bk",
    TAIL: "Tl",
    MIDDLE: "Md",
    ABOVE: "Ab",
    UNDER: "Un",
    NONE: ''
}

/**
 * View axis for limbs.
 * @enum {int}
 */
OCO.View = {
    FRONT: 0,
    LEFT: 1,
    RIGHT: 2,
    BACK: 3,
    TOP: 4,
    BOTTOM: 5
}

/**
 * How images are encoded in the OCO file
 * @enum {string}
 */
OCO.ImageEncoding = {
    PNG_BASE64: 'PNG/BASE64',
    RELATIVE_PATH: 'PATH',
    ABSOLUTE_PATH: 'ABS_PATH'
}

/**
 * The path where the OCO Library is stored.
 * This is in the User Documents/OCO/OCO Library if the script has write access in this folder.<br/>
 * In rare cases on MacOS we can't write files in the documents folder. In this case the default library path in a temp folder.<br/>
 * You can change this path to a custom path before using the OCO API.
 * @type {string}
 */
OCO.libraryPath = "";

// === PRIVATE ===

// Gets a value or a default value if it is undefined
OCO._d = function( val, defaultVal )
{
	return typeof val !== 'undefined' ? val : defaultVal;
}

OCO._escapeRegEx = function (string)
{
	return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

// Generates a unique string
OCO._uniqueString = 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(OCO._escapeRegEx(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 = OCO._numberToString(greatestNumber, numDigits);
        newString += greatestNumber;
    }

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

    return newString;
}

// Converts a number to a string, with leading zeroes
OCO._numberToString = function (num, numDigits, base)
{
    if (base == undefined) base = 10;
	var result = num.toString(base);
	while(numDigits > result.length)
	{
		result  = "0" + result ;
	}
	return result;
}

// Interpolates a value
OCO._linearInterpolation = 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 = OCO._clamp(value, targetMin, targetMax);
    return result;
}

// Clamps the value
OCO._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];
}

// Checks if the script can write files in the folder
OCO._canWrite = function( folder ) {
    if ( !(folder instanceof 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;
}

// Encodes a file as a base64 string
OCO._base64 = function( file ) {
    if (!(file instanceof File)) file = new File(file);
    if (!file.exists) return "";

    file.encoding = "binary";
    file.open("r");
    var bin = file.read();
    file.close();

    var ecdStr = Base64.btoa(bin);

    return ecdStr;
}

// Checks if this is a valid base64 encoding
OCO._checkBase64 = function( b64 ) {
    var len = b64.length;

    if (len % 4 > 0) {
        return false;
    }

    // If there's a dot, that's probably a file name
    if ( b64.indexOf(".") >= 0 )
        return false;

    return true;
}

// Decodes base64 content to a file; returns the File object or null
OCO._fromBase64 = function( b64, file )
{
    if (b64 == "") return null;
    if (!DuFile.checkBase64(b64)) return null;

    // Convert to bin
    var bin = Base64.atob(b64);

    // Write to file
    if (!(file instanceof File)) file = new File( file );
    file.encoding = 'BINARY';

    try
	{
        if (!file.open('w')) return null;
        file.write(bin);
		file.close();
		return file;
    }
    catch (e) { return null; }
}

/**
 * Gets the boundaries of an armature (a chain of bones)
 * @param {OCOBone[]} armature The chain of bones
 * @returns {float[]} [left, top, right, bottom]
 */
OCO.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 = OCO.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];
}

// Makes a path relative to a file
OCO._relativePath = function ( path, destinationFile )
{
    if (!(destinationFile instanceof File)) destinationFile = new File(destinationFile);
    var sourceFile = new File( path );
    var sourceFolder = sourceFile.parent;
    var destFolder = destinationFile.parent;
    var destPath = destFolder.absoluteURI;
    var sourcePath = sourceFolder.absoluteURI;
    var count = 0;
    while (sourcePath.indexOf(destPath) != 0)
    {
        // Up one folder and check again
        count++;
        destFolder = destFolder.parent;
        if (!destFolder) destPath = "";
        else destPath = destFolder.absoluteURI;
    }
    // Add ups
    var relativePath = "";
    for (var i = 0; i < count; i++)
    {
        relativePath += '../';
    }
    if (destPath.length > 0)
        relativePath += sourcePath.substr( destPath.length + 1 ) + '/';
    else
        relativePath += sourcePath + '/';
    relativePath += sourceFile.name;
    return relativePath;
}

// Makes a path absolute
OCO._absolutePath = function ( path, relativeTo )
{
    if (!(relativeTo instanceof File)) relativeTo = new File(relativeTo);
    var absFolder = relativeTo.parent;
    while (path.indexOf("../") == 0)
    {
        path = path.substr(3);
        absFolder = absFolder.parent;
    }
    var absPath = absFolder.absoluteURI + '/' + path;
    return absPath;
}

OCO._parseJsonFile = function (file )
{
    if (!(file instanceof File)) file = new File(file);
    file.encoding = 'UTF-8';
    if (!file.open('r')) return null;
    var json = file.read();
    file.close();
    if (json == '') return null;
    var data = {};
    try { data  = JSON.parse(json); } catch (e) { return null; }
    return data;
}

/**
 * Measures the vector length between two points
 * @param {int[]} value1 - The first value
 * @param {int[]} value2 - The second value
 * @return {float} The length
 */
OCO._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);
}

// === INIT ===
if ( OCO._canWrite( Folder.myDocuments ) ) OCO.libraryPath = Folder.myDocuments.absoluteURI + "/OCO/OCO Library";
else OCO.libraryPath = Folder.temp.absoluteURI + "/OCO/OCO Library";

/**
 * 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 OCO
 */
function OCODoc(name)
{
    /**
     * The type of this OCO Doc
     * @name type
     * @memberof OCODoc
     * @type {string}
     */
    this.type = OCO.Type.META;
    /**
     * The name of this OCO Doc
     * @name name
     * @memberof OCODoc
     * @type {string}
     */
    this.name = name;
    /**
     * The height of the character. Should always be 1!
     * @name height
     * @memberof OCODoc
     * @type {float}
     */
    this.height = 185.0;
    /**
     * The width of the character.
     * @name width
     * @memberof OCODoc
     * @type {float}
     */
    this.width = 60.0,
    /**
     * The coordinates of the center of mass of the character. [X, Y].
     * @name centerOfMass
     * @memberof OCODoc
     * @type {float[]}
     */
    this.centerOfMass = [0.0, 107.0];
    /**
     * The limbs/ Armatures
     * @name limbs
     * @memberof OCOLimb
     * @type {OCOLimb[]}
     */
    this.limbs = [];
    /**
     * The resolution, in pixels, of the document
     * @type {int[]}
     */
    this.resolution = [1920,1080];
    /**
     * The world origin in pixels in the document
     * @type {float[]}
     */
    this.world = [960,980];
    /**
     * The definition
     * @type {float}
     */
    this.pixelsPerCm = 4.22;
    /**
     * An icon or thumbnail path
     * @type {string}
     */
    this.icon = ""
    /**
     * How images should be encoded when exporting the doc to a file/folder
     * @type {OCO.ImageEncoding}
     */
    this.imageEncoding = OCO.ImageEncoding.PATH;

    // Private: Converts a pixel coordinate relative to the world to a coordinate relative to the top left corner
    this.fromWorld = function( point )
    {
        var x = point[0] + this.world[0];
        var y = this.world[1] - point[1];
        return [x,y];
    }

    // Private: Converts a pixel coordinate relative to the top left corner to a coordinate relative to the world
    this.toWorld = function( point )
    {
        var x = point[0] - this.world[0];
        var y = this.world[1] - point[1];
        return [x, y];
    }
}

/**
    * Counts the total number of bones in this doc
    * @return {int}
    */
OCODoc.prototype.numBones = function ()
{
    var count = 0;
    for (var i = 0, ni = this.limbs.length; i < ni; i++ )
    {
        count += this.limbs[i].numBones();
    }
    return count;
}

/**
 * Gets the boundaries of the doc
 * @returns {float[]} [left, top, right, bottom]
 */
OCODoc.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.
 */
OCODoc.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 {OCO.Limb} [limb=OCO.Limb.CUSTOM] A Predefined limb
 * @param {OCO.Side} [side=OCO.Side.NONE] - The side of the limb
 * @param {OCO.Location} [location=OCO.Location.NONE] - The location of the limb
 * @param {OCO.LimbType} [type=OCO.LimbType.CUSTOM] - The type of the limb
 * @return {OCOLimb} The new limb
 */
OCODoc.prototype.newLimb = function( limb, side, location, type )
{
    var l = new OCOLimb( limb, side, location, type );
    this.limbs.push(l);
    return l;
}

/**
 * Creates a new arm.
 * @param {OCO.LimbType} [type=OCO.LimbType.HOMINOID] The type of limb
 * @param {OCO.Side} [side=OCO.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 {OCO.Location} [location=OCO.Location.FRONT] The location of the limb
 * @param {OCO.View} [view] The view
 * @returns {OCOLimb} The arm
 */
OCODoc.prototype.newArm = function( type, side, shoulder, arm, forearm, hand, claws, position, location, view )
{
    side = OCO._d(side, OCO.Side.LEFT);
    type = OCO._d(type, OCO.LimbType.HOMINOID);
    location = OCO._d(location, OCO.Location.FRONT);

    shoulder = OCO._d( shoulder, false );
    arm = OCO._d( arm, true );
    forearm = OCO._d( forearm, true );
    hand = OCO._d( hand, true );
    claws = OCO._d( claws, false );

    // Unit
    var u = this.height;

    // type
    var hum = type == OCO.LimbType.HOMINOID;
    var hum = type == OCO.LimbType.PLANTIGRADE && !claws;
    var plan = type == OCO.LimbType.PLANTIGRADE && claws;
    var dig = type == OCO.LimbType.DIGITIGRADE;
    var ung = type == OCO.LimbType.UNGULATE;
    var artF = type == OCO.LimbType.ARTHROPOD && location != OCO.Location.MIDDLE && location != OCO.Location.BACK;
    var artM = type == OCO.LimbType.ARTHROPOD && location == OCO.Location.MIDDLE;
    var artB = type == OCO.LimbType.ARTHROPOD && location == OCO.Location.BACK;
    if ( !hum && !plan && !dig && !ung && !artF && !artM && !artB ) hum = true;

    // View
    if (typeof view === 'undefined')
    {
        if (type == OCO.LimbType.PLANTIGRADE && !claws) view = OCO.View.FRONT;
        else view = OCO.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( OCO.Limb.ARM, side, location, type );
    var b = limb.newArmature( i18n._("Arm"), num );

    // side
    var s = 1;
    if ( side == OCO.Side.RIGHT && view == OCO.View.FRONT ) s = -1;
    else if ( side == OCO.Side.LEFT && view == OCO.View.BACK ) s = -1;
    else if ( view == OCO.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 (typeof position !== 'undefined' )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    if ( shoulder )
    {
        b.name = i18n._("Shoulder");
        b.type = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO.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 OCOBone( i18n._("Heel") );
        heelBone.type = OCO.Bone.HEEL;
        heelBone.x = s * u * .38 + ox;
        heelBone.y = 0 + oy;
        b.children.push(heelBone);
    }

    return limb;
}

/**
 * Creates a new leg.
 * @param {OCO.LimbType} [type=OCO.LimbType.HOMINOID] The type of limb
 * @param {OCO.Side} [side=OCO.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 {OCO.Location} [location=OCO.Location.BACK] The location of the limb
 * @param {OCO.View} [view] The view
 * @returns {OCOLimb} The leg
 */
OCODoc.prototype.newLeg = function( type, side, thigh, calf, foot, claws, position, location, view )
{
    side = OCO._d(side, OCO.Side.LEFT);
    type = OCO._d(type, OCO.LimbType.HOMINOID);
    location = OCO._d(location, OCO.Location.BACK);

    thigh = OCO._d( thigh, true );
    calf = OCO._d( calf, true );
    foot = OCO._d( foot, true );
    claws = OCO._d( claws, false );

    // Unit
    var u = this.height;

    // type
    var plan = type == OCO.LimbType.PLANTIGRADE;
    var dig = type == OCO.LimbType.DIGITIGRADE;
    var ung = type == OCO.LimbType.UNGULATE;
    var hum = type == OCO.LimbType.HOMINOID;

    // View
    if (typeof view === 'undefined')
    {
        if (type == OCO.LimbType.PLANTIGRADE) view = OCO.View.FRONT;
        else view = OCO.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( OCO.Limb.LEG, side, location, type );
    var b = limb.newArmature( i18n._("Leg"), num );

    // side
    var s = 1;

    if ( side == OCO.Side.RIGHT && view == OCO.View.FRONT ) s = -1;
    else if ( side == OCO.Side.LEFT && view == OCO.View.BACK ) s = -1;
    else if ( view == OCO.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 (typeof position !== 'undefined' )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    if ( thigh )
    {
        b.name = i18n._("Thigh");
        b.type = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO.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 OCOBone( i18n._("Heel") );
        heelBone.type = OCO.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 {OCO.View} [view] The view
 * @returns {OCOLimb} The spine
 */
OCODoc.prototype.newSpine = function( head, neck, spine, hips, position )
{
    head = OCO._d( head, true );
    neck = OCO._d( neck, 1 );
    spine = OCO._d( spine, 2 );
    hips = OCO._d( 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( OCO.Limb.SPINE, OCO.Side.NONE, OCO.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 (typeof position !== 'undefined' )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // for each part, set the location
    if ( hips )
    {
        b.name = i18n._("Hips");
        b.type = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO.Bone.TIP;
    b.x = ox;
    b.y = u + oy;

    return limb;
}

/**
 * Creates a new tail.
 * @param {int} [numBones=3] Number of tail bones
 * @returns {OCOLimb} The spine
 */
OCODoc.prototype.newTail = function( numBones )
{
    numBones = OCO._d( 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( OCO.Limb.TAIL, OCO.Side.NONE, OCO.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 = OCO.Bone.TAIL_ROOT;
        else if ( i <= (n-1)*2 / 3) b.type = OCO.Bone.TAIL_MID;
        else b.type = OCO.Bone.TAIL_END;
        
        if (b.children.length > 0) b = b.children[0];
    }

    b.name = i18n._("Tail") +
        '_' + i18n._("Tip");
    b.type = OCO.Bone.TIP;

    return limb;
}

/**
 * Creates a new hair strand.
 * @param {int} [numBones=3] Number of hair bones
 * @returns {OCOLimb} The hair
 */
OCODoc.prototype.newHairStrand = function( numBones )
{
    numBones = OCO._d( 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( OCO.Limb.HAIR, OCO.Side.NONE, OCO.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 = OCO.Bone.HAIR_ROOT;
        else if ( i <= (n-1)*2 / 3) b.type = OCO.Bone.HAIR_MID;
        else b.type = OCO.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 = OCO.Bone.TIP;
    b.x = 0;
    b.y = 50;

    return limb;
}

/**
 * Creates a new wing.
 * @param {OCO.Side} [side=OCO.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 {OCO.View} [view=OCO.View.TOP] The view
 * @returns {OCOLimb} The wing
 */
OCODoc.prototype.newWing = function( side, arm, forearm, hand, feathers, position, view )
{
    side = OCO._d(side, OCO.Side.LEFT);
    view = OCO._d(view, OCO.View.TOP);

    arm = OCO._d( arm, true );
    forearm = OCO._d( forearm, true );
    hand = OCO._d( hand, true );
    feathers = OCO._d( 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( OCO.Limb.WING, side, OCO.Location.NONE );
    var b = limb.newArmature( i18n._("Wing"), num );

    // side
    var s = 1;
    if ( side == OCO.Side.RIGHT && (view == OCO.View.TOP || view == OCO.View.BACK) ) s = -1;
    else if ( side == OCO.Side.LEFT && (view == OCO.View.BOTTOM || view == OCO.View.FRONT) ) s = -1;
    else if ( view == OCO.View.LEFT ) s = -1;

    // arm position
    var x, y;
    if ( view == OCO.View.TOP || view == OCO.View.BOTTOM )
    {
        x = s * u * -.12;
        y = u * .64;
    }
    else return limb; // not supported yet
    var ox = 0;
    var oy = 0;

    // offset
    if (typeof position !== 'undefined' )
    {
        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 = OCO.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 = OCO.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 = OCO.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 = OCO.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 = OCO._uniqueString( i18n._("Feather"), names);
        b = new OCOBone( boneName );
        b.type = OCO.Bone.FEATHER;
        b.x = s * u * xr + ox;
        b.y = u * yr + oy;

        // tip
        var boneName = OCO._uniqueString( i18n._("Feather") +
            '_' + i18n._("Tip"), names);
        var bt = new OCOBone( boneName );
        bt.type = OCO.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 = OCO._linearInterpolation( i, 0, n/2, -.54, -.38);
            var y = OCO._linearInterpolation( i, 0, n/2, .66, .63);
            var xt = OCO._linearInterpolation( i, 0, n/2, -.96, -.56);
            var yt = OCO._linearInterpolation( i, 0, n/2, .62, .28);
            createFeather( x, y, xt, yt, parent2 );
        }
        else 
        {
            var x = OCO._linearInterpolation( i, n/2, n-1, -.38, -.22);
            var y = OCO._linearInterpolation( i, n/2, n-1, .63, .51);
            var xt = OCO._linearInterpolation( i, n/2, n-1, -.56, -.08);
            var yt = OCO._linearInterpolation( 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 {OCOLimb} The snake spine
 */
OCODoc.prototype.newSnakeSpine = function( head, spine, position )
{
    head = OCO._d( head, true );
    spine = OCO._d( 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( OCO.Limb.SNAKE_SPINE, OCO.Side.NONE, OCO.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 (typeof position !== 'undefined' )
    {
        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 = OCO.Bone.SNAKE_SPINE_ROOT;
        else if ( i <= (spine-1)*2 / 3) b.type = OCO.Bone.SNAKE_SPINE_MID;
        else b.type = OCO.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 = OCO.Bone.TIP;
    b.x = -u *.75;
    b.y = y + oy;

    // Head
    if (head)
    {
        var b = new OCOBone( i18n._("Head") );
        b.type = OCO.Bone.SKULL;
        b.x = x;
        b.y = y;

        var s = limb.armature.pop();

        limb.armature.push(b);
        b.children.push(s);

        var bt = new OCOBone( i18n._("Head") +
            '_' + i18n._("Tip") );
        bt.type = OCO.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 {OCOLimb} The fish spine
 */
OCODoc.prototype.newFishSpine = function( head, spine, position )
{
    head = OCO._d( head, true );
    spine = OCO._d( 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( OCO.Limb.FISH_SPINE, OCO.Side.NONE, OCO.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 (typeof position !== 'undefined')
    {
        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 = OCO.Bone.FISH_SPINE_ROOT;
        else if ( i <= (spine-1)*2 / 3) b.type = OCO.Bone.FISH_SPINE_MID;
        else b.type = OCO.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 = OCO.Bone.TIP;
    b.x = -u *.25;
    b.y = y + oy;

    // Head
    if (head)
    {
        var b = new OCOBone( i18n._("Head") );
        b.type = OCO.Bone.SKULL;
        b.x = x;
        b.y = y;

        var s = limb.armature.pop();

        limb.armature.push(b);
        b.children.push(s);

        var bt = new OCOBone( i18n._("Head") +
            '_' + i18n._("Tip") );
        bt.type = OCO.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 {OCO.Side} [side=OCO.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 {OCO.View} [view=OCO.View.RIGHT] The view
 * @returns {OCOLimb} The fin
 */
OCODoc.prototype.newFin = function( side, fishbones, view, position )
{
    side = OCO._d(side, OCO.Side.LEFT);
    view = OCO._d(view, OCO.View.RIGHT);

    fishbones = OCO._d( fishbones, 5 );

    // Unit
    var u = this.height;

    // Create limb
    var limb = this.newLimb( OCO.Limb.FIN, side, OCO.Location.NONE );

    // side
    var s = 1;
    if ( side == OCO.Side.RIGHT && (view == OCO.View.TOP || view == OCO.View.BACK) ) s = -1;
    else if ( side == OCO.Side.LEFT && (view == OCO.View.BOTTOM || view == OCO.View.FRONT) ) s = -1;
    else if ( view == OCO.View.LEFT ) s = -1;

    // arm position
    var x = s * u * -.17;
    var y = u * .47;
    var ox = 0;
    var oy = 0;

    // offset
    if ( typeof position !== 'undefined' )
    {
        ox = position[0] - x;
        oy = position[1] - y;
    }

    // Create root
    var rootBone = new OCOBone( i18n._("Fin") );
    rootBone.type = OCO.Bone.FIN;
    rootBone.x = x + ox;
    rootBone.y = y + oy;
    limb.armature.push(rootBone);
    // Tip
    var bt = new OCOBone( i18n._("Fin") +
        '_' + i18n._("Tip") );
    bt.type = OCO.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 = OCO._uniqueString( i18n._("Fishbone"), names);
        b = new OCOBone( boneName );
        b.type = OCO.Bone.FIN_FISHBONE;
        b.x = s * u * xr + ox;
        b.y = u * yr + oy;

        // tip
        var boneName = OCO._uniqueString( i18n._("Fishbone") +
            '_' + i18n._("Tip"), names);
        var bt = new OCOBone( boneName );
        bt.type = OCO.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 = OCO._linearInterpolation( i, 0, n-1, -.24, -.15);
        var y = OCO._linearInterpolation( i, 0, n-1, .63, .41);
        var xt = OCO._linearInterpolation( i, 0, n-1, -.82, -.17);
        var yt = OCO._linearInterpolation( i, 0, n-1, .60, .29);
        createFishbone( x, y, xt, yt );
    }

    return limb;
}

/**
 * Converts a doc coordinate/value in centimeters to pixel coordinates.<br/>
 * For multidimensionnal values (coordinates), the origin is adjusted from doc to image.
 * @param {float[]} point The coordinate to convert
 * @returns {int[]} The coordinates in pixels
 */
OCODoc.prototype.toPixels = function( point )
{
    if (point.length)
    {
        // First, convert to pixels
        var x = point[0]*this.pixelsPerCm;
        var y = point[1]*this.pixelsPerCm;
        // We're relative to the world, adjust
        return this.fromWorld( [x, y] );
    }
    return point * this.pixelsPerCm;
}

/**
 * Converts coordinates in pixels in the world to centimeters coordinates in the doc<br/>
 * For multidimensionnal values (coordinates), the origin is adjusted image to doc.
 * @param {float[]|float} point The coordinate to convert
 * @returns {float[]|float} The coordinates in centimeters relative to the doc
 */
OCODoc.prototype.fromPixels = function( point )
{
    if (point.length)
    {
        // We need to be relative to the world
        point = this.toWorld(point);
        var x = point[0];
        var y = point[1];
        // Convert to cm
        x /= this.pixelsPerCm;
        // Y coordinate is at bottom in OCO, top in 2D docs
        y /= this.pixelsPerCm;
        return [x, y];
    }
    return point / this.pixelsPerCm;
}

/**
 * Creates a js object containing this document data.<br/>
 * This object could then be exported to JSON for example.
 * @return {Object} the JS Object
 */
OCODoc.prototype.toObject = function()
{
    var data = {
        'type': this.type,
        'name': this.name,
        'height': this.height,
        'width': this.width,
        'centerOfMass': this.centerOfMass,
        'limbs': [],
        'resolution': this.resolution,
        'world': this.world,
        'pixelsPerCm': this.pixelsPerCm,
        'icon': this.icon,
        'imageEncoding': this.imageEncoding
    }

    for (var i = 0, n  = this.limbs.length; i < n; i++)
    {
        var limbData = this.limbs[i].toObject();
        data['limbs'].push(limbData);
    }

    return data;
}

/**
 * Creates a JSON string representing this document
 * @param {OCO.ImageEncoding} [imageEncoding=OCO.ImageEncoding.PATH] How to encode images in the OCO File
 * @param {File} [destinationFile] If imageEncoding is `OCO.ImageEncoding.PATH`, you must provide the OCO file to make the paths relative to it.
 * @return {string} the JSON document
 */
OCODoc.prototype.toJson = function( destinationFile )
{
    var data  = this.toObject();

    if (data.imageEncoding == OCO.ImageEncoding.PNG_BASE64) {
        data.icon = OCO._base64( data.icon );
    }
    else if (data.imageEncoding == OCO.ImageEncoding.PATH) {
        data.icon = OCO._relativePath( data.icon, destinationFile );
    }
    else if (data.imageEncoding == OCO.ImageEncoding.ABS_PATH) {
        var iconFile = new File( data.icon );
        data.icon = iconFile.absoluteURI();
    }

    return JSON.stringify(data, null, 4);
}

/**
 * Exports the current document to an oco file
 * @param {File|string} file The file.
 * @return {File} the file.
 */
OCODoc.prototype.toFile = function ( file )
{
    if (!(file instanceof File)) file = new File(file);
    file.encoding = 'UTF-8';
    var data = this.toJson( );

    if (!file.open('w')) return file;
    file.write(data);
    file.close();

    return file;
}

/**
 * Extracts the icon from the OCO file
 * @param {string|File} file The OCO file
 * @param {string|File} [destination] The destination file if the file is included in the OCO file. Next to the OCO file by default.
 * @return {File} The icon file, or null if there was no icon/the file could not be written.
 */
OCODoc.extractIcon = function( file, destination )
{
    if (!(file instanceof File)) file = new File(file);

    // Check if there's an icon to extract
    var data = OCO._parseJsonFile( file );
    if (data.icon == "") return null;
    if (data.imageEncoding == OCO.ImageEncoding.ABS_PATH) return new File(data.icon);
    if (data.imageEncoding == OCO.ImageEncoding.PATH)
    {
        destination = OCO._absolutePath( data.icon, file);
        return new File(destination);
    }
    if (data.icon != "" && data.imageEncoding == OCO.ImageEncoding.PNG_BASE64) 
    {
        destination = OCO._d(destination, file.parent.absoluteURI + "/" + file.displayName.replace(".oco", ".png"));
        return OCO._fromBase64(data.icon, destination);
    }
    return null;
 
}

/**
 * Creates a new doc by reading a file
 * @param {File|string} file The file
 * @return {OCODoc|null} The document or null if the file couldn't be read or parsed
 */
OCODoc.fromFile = function ( file )
{
    var data = OCO._parseJsonFile( file );
    
    var doc = new OCODoc( OCO._d( data.name, "Character" ) );
    doc.type = OCO._d( data.type, OCO.Type.META );
    doc.height = OCO._d( data.height, 185 );
    doc.width = OCO._d( data.width, 60 );
    doc.centerOfMass = OCO._d( data.centerOfMass, [0.0, 107.0] );
    doc.resolution = OCO._d( data.resolution, [1920, 1080] );
    doc.world = OCO._d( data.world, [960, 980] );
    doc.pixelsPerCm = OCO._d( data.pixelsPerCm, 4.22 );
    doc.icon = OCO._d( data.icon, "" );
    doc.imageEncoding = OCO._d( data.imageEncoding, OCO.ImageEncoding.PATH );

    // Check Data
    if (doc.resolution[0] <= 0 || doc.resolution[0] > 30000)
        throw "Invalid resolution; the width must be in the range [0, 30000]";

    if (doc.resolution[1] <= 0 || doc.resolution[1] > 30000)
        throw "Invalid resolution; the height must be in the range [0, 30000]";


    // Parse limbs
    data.limbs = OCO._d( data.limbs, [] );
    for (var i = 0, ni = data.limbs.length; i < ni; i++)
    {
        doc.limbs.push( OCOLimb.fromObject(data.limbs[i]) );
    }


    return doc;
}

/**
 * Creates a new OCO Limb.
 * @class
 * @classdesc A limb contained in an OCO Doc.
 * @param {OCO.Limb} [limb=OCO.Limb.CUSTOM] A Predefined limb
 * @param {OCO.Side} [side=OCO.Side.NONE] - The side of the limb
 * @param {OCO.Location} [location=OCO.Location.NONE] - The location of the limb
 * @param {OCO.LimbType} [type=OCO.LimbType.CUSTOM] - The type of the limb
 * @category OCO
 */
function OCOLimb(limb, side, location, type )
{
    /**
     * The predefined limb
     * @name limb
     * @memberof OCOLimb
     * @type {OCO.Limb}
     */
    this.limb = OCO._d(limb, OCO.Limb.CUSTOM);
    /**
     * The type of the limb
     * @name type
     * @memberof OCOLimb
     * @type {OCO.LimbType}
     */
    this.type = OCO._d(type, OCO.LimbType.CUSTOM);
    /**
     * The sided of the limb
     * @name side
     * @memberof OCOLimb
     * @type {OCO.Side}
     */
    this.side = OCO._d( side, OCO.Side.NONE);
    /**
     * The location of the limb
     * @name location
     * @memberof OCOLimb
     * @type { OCO.Location}
     */
    this.location = OCO._d( location, OCO.Location.NONE);
    /**
     * The bones of the limb
     * @name location
     * @memberof OCOLimb
     * @type {OCOBone[]}
     */
    this.armature = [];
}

/**
    * Counts the total number of bones in this limb
    * @return {int}
    */
OCOLimb.prototype.numBones = function ()
{
    var count = 0;
    for (var i = 0, ni = this.armature.length; i < ni; i++ )
    {
        count += this.armature[i].numBones();
    }
    return count;
}

/**
 * Gets the boundaries of the limb
 * @returns {float[]} [left, top, right, bottom]
 */
OCOLimb.prototype.bounds = function( )
{
    return OCO.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 {OCOBone} The root bone.
 */
OCOLimb.prototype.newArmature = function( name, num, length )
{
    num = OCO._d(num, 2);
    length = OCO._d(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 = OCO._uniqueString(name, names);
        var bone = new OCOBone( 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 js object containing this limb data.<br/>
 * This object could then be exported to JSON for example.
 * @return {Object} The JS Object
 */
OCOLimb.prototype.toObject = function()
{
    var data = {
        'limb': this.limb,
        'type': this.type,
        'side': this.side,
        'location': this.location,
        'armature': []
    }

    for (var i = 0, n = this.armature.length; i < n; i++)
    {
        var boneData = this.armature[i].toObject();
        data['armature'].push(boneData);
    }

    return data;
}

/**
 * Creates a limb from a js object.
 * @param {Object} data The js object representing the limb
 * @return {OCOLimb} The new limb
 */
OCOLimb.fromObject = function( data )
{
    var limb = OCO._d( data.limb, OCO.Limb.CUSTOM);
    var side = OCO._d( data.side, OCO.Side.NONE);
    var location = OCO._d( data.location, OCO.Location.NONE);
    var type = OCO._d( data.type, OCO.LimbType.CUSTOM);
    var l = new OCOLimb( limb, side, location, type);

    // Parse armature
    data.armature = OCO._d( data.armature, [] );
    for (var i = 0, ni = data.armature.length; i < ni; i++)
    {
        l.armature.push( OCOBone.fromObject( data.armature[i] ) );
    }
    return l;
}

/**
 * Creates a new OCO Bone.
 * @class
 * @classdesc A bone contained in an OCO Limb.
 * @param {string} name The name
 * @category OCO
 */
function OCOBone(name)
{
    /**
     * The name of the bone
     * @name name
     * @memberof OCOBone
     * @type {string}
     */
    this.name = name;
    /**
     * The x coordinate of the bone
     * @name x
     * @memberof OCOBone
     * @type {float}
     */
    this.x = 0.0;
    /**
     * The y coordinate of the bone
     * @name y
     * @memberof OCOBone
     * @type {float}
     */
    this.y = 0.0;
    /**
     * true if this bone is attached to its parent.
     * @name attached
     * @memberof OCOBone
     * @type {Boolean}
     */
    this.attached = false;
    /**
     * The child bones.
     * @name children
     * @memberof OCOBone
     * @type {OCOBone[]}
     */
    this.children = [];
    /**
     * The child limbs.
     * @name limbs
     * @memberof OCOBone
     * @type {OCOLimb[]}
     */
    this.limbs = [];
    /**
     * The type of bone.
     * @name type
     * @memberof OCOBone
     * @type {OCO.Bone}
     */
    this.type = OCO.Bone.CUSTOM;
    /**
     * The envelop of the bone,<br/>
     * In a meta rig, this is a silhouette which will contain the design,
     * and can be used to help locate the joint, link the design to the bone, etc.<br/>
     * In a rigged character, this should be a simple silhouette close to the artwork silhouette.
     */
    this.envelop = {};
    /**
     * The width of the envelop, around the joint
     * @type {float}
     */
    this.envelop.width = 0.0;
    /**
     * The offset of the envelop, in the local X-axis<br/>
     * 0.0 is centered around the joint,<br/>
     * positive values are on the right of the joint when the bone is oriented towards the ground,<br/>
     * negative values are on the left of the joint when the bone is oriented towards the ground.
     * @type {float}
     */
    this.envelop.offset = 0.0;
}

/**
 * The length of the bone (this distance with its first child)
 * @return {int} The length
 */
OCOBone.prototype.length = function ()
{
    if (this.children.length == 0) return 0;
    var child = this.children[0];
    return OCO._length([this.x, this.y], [child.x, child.y]);
}

/**
    * Counts the total number of child bones
    * @return {int}
    */
OCOBone.prototype.numBones = function ()
{
    var count = 1;
    for (var i = 0, ni = this.children.length; i < ni; i++ )
    {
        count += this.children[i].numBones();
    }
    for (var i = 0, ni = this.limbs.length; i < ni; i++ )
    {
        count += this.limbs[i].numBones();
    }
    return count;
}

/**
 * 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
 */
OCOBone.prototype.translate = function( x, y, translateChildren ) {
    translateChildren = OCO._d(translateChildren, true);
    x = OCO._d(x, 0);
    y = OCO._d(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
 */
OCOBone.prototype.translateTo = function( x, y, translateChildren ) {
    translateChildren = OCO._d(translateChildren, true);
    x = OCO._d(x, this.x);
    y = OCO._d(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);
        }
    }
}

/**
 * Creates a js object containing this bone data.<br/>
 * This object could then be exported to JSON for example.
 * @return {Object} the JS Object
 */
OCOBone.prototype.toObject = function()
{
    var data = {
        'name': this.name,
        'x': this.x,
        'y': this.y,
        'attached': this.attached,
        'children': [],
        'limbs': [],
        'type': this.type
    }
    data.envelop = {};
    data.envelop.width = this.envelop.width;
    data.envelop.offset = this.envelop.offset;

    for (var i = 0, n = this.children.length; i < n; i++)
    {
        var boneData = this.children[i].toObject();
        data['children'].push(boneData);
    }

    for (var i = 0, n = this.limbs.length; i < n; i++)
    {
        var limbData = this.limbs[i].toObject();
        data['limbs'].push(limbData);
    }

    return data;
}

/**
 * Creates a bone from a js object.
 * @param {Object} data The js object representing the bone
 * @return {OCOBone} The new bone
 */
OCOBone.fromObject = function( data )
{
    var bone = new OCOBone( OCO._d(data.name, "Bone"));
    bone.x = OCO._d(data.x, 0.0);
    bone.y = OCO._d(data.y, 0.0);
    bone.attached = OCO._d(data.attached, false);
    bone.type = OCO._d(data.type, OCO.Bone.CUSTOM);
    data.envelop = OCO._d(data.envelop, {});
    bone.envelop.width = OCO._d(data.envelop.width, bone.envelop.width);
    bone.envelop.offset = OCO._d(data.envelop.offset, bone.envelop.offset);


    // Parse children
    data.children = OCO._d(data.children, []);
    for( var i = 0, ni = data.children.length; i < ni; i++)
    {
        bone.children.push( OCOBone.fromObject(data.children[i]) );
    }

    // Parse limbs
    data.limbs = OCO._d(data.limbs, []);
    for (var i = 0, ni = data.limbs.length; i < ni; i++)
    {
        bone.limbs.push( OCOLimb.fromObject(data.limbs[i]) );
    }

    return bone;
}//*/
// ==================== |---------| ====================
// ==================== | presets | ====================
// ==================== |---------| ====================

// ==================== |-------------| ====================
// ==================== | preset_null | ====================
// ==================== |-------------| ====================

var preset_null = new DuBinary( "RIFX\x00\x00\u00BF\u00BEFaFXhead\x00\x00\x00\x10\x00\x00\x00\x03\x00\x00\x00]\x00\x00\x00\x19\x01\x00\x00\x00LIST\x00\x00\u00BF\u009Abescbeso\x00\x00\x008\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00x\x00\x00\x1E\x00\x00\x00\x00\x00\x04\x00\x01\x00\x01\x048\x048?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\u00FF\u00FF\u00FF\u00FFLIST\x00\x00\x00dtdsptdot\x00\x00\x00\x04\u00FF\u00FF\u00FF\u00FFtdpl\x00\x00\x00\x04\x00\x00\x00\x01LIST\x00\x00\x00@tdsitdix\x00\x00\x00\x04\u00FF\u00FF\u00FF\u00FFtdmn\x00\x00\x00(ADBE Root Vectors Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdsn\x00\x00\x00\x10Utf8\x00\x00\x00\bContentsLIST\x00\x00\x00dtdsptdot\x00\x00\x00\x04\u00FF\u00FF\u00FF\u00FFtdpl\x00\x00\x00\x04\x00\x00\x00\x01LIST\x00\x00\x00@tdsitdix\x00\x00\x00\x04\u00FF\u00FF\u00FF\u00FFtdmn\x00\x00\x00(ADBE End of path sentinel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\u00BE^tdgptdsb\x00\x00\x00\x04\x00\x00\x04\x01tdsn\x00\x00\x00\x10Utf8\x00\x00\x00\bContentstdmn\x00\x00\x00(ADBE Vector Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00ODtdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06Anchortdmn\x00\x00\x00(ADBE Vector Blend Mode\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vectors Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\n\u00B2tdgptdsb\x00\x00\x00\x04\x00\x00\x04\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Shape - Ellipse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x03\u00E4tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x16Utf8\x00\x00\x00\x0EEllipse Path 1tdmn\x00\x00\x00(ADBE Vector Shape Direction\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Ellipse Size\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\"tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00P@\x10\x00\x00\x00\x00\x00\x00@\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\u00C0\u00DF@\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\u00DF@\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Ellipse Position\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00E2tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x0F\x00\x03\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Graphic - Fill\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x06\btdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06Fill 1tdmn\x00\x00\x00(ADBE Vector Blend Mode\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Composite Order\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Fill Rule\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Fill Color\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Fill Opacity\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Transform Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\bltdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Anchor\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00E2tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x0F\x00\x03\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Position\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00E2tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x0F\x00\x03\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Scale\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\"tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00P@Y\x00\x00\x00\x00\x00\x00@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\u00C0\u00DF@\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\u00DF@\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Skew\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\u00C0U@\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@U@\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Skew Axis\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Rotation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Group Opacity\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Materials Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00:\x16tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vec3D Front RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00nRtdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\fUtf8\x00\x00\x00\x04Icontdmn\x00\x00\x00(ADBE Vector Blend Mode\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vectors Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00)\u00C2tdgptdsb\x00\x00\x00\x04\x00\x00\x04\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Shape - Rect\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x05\x18tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x18Utf8\x00\x00\x00\x10Rectangle Path 1tdmn\x00\x00\x00(ADBE Vector Shape Direction\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Rect Size\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\"tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00P@Y\x00\x00\x00\x00\x00\x00@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\u00C0\u00DF@\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\u00DF@\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Rect Position\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00E2tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x0F\x00\x03\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Rect Roundness\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Graphic - Stroke\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00#\u00E4tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x10Utf8\x00\x00\x00\bStroke 1tdmn\x00\x00\x00(ADBE Vector Blend Mode\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Composite Order\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Color\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@g\u009F`H\u00A0\x00\x00@G\x7F\u008D[\u0080\x00\x00@h\x7F\u00C6\u00A1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Opacity\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Width\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Line Cap\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Line Join\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Miter Limit\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Dashes\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\b\u00B4tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Stroke Dash 1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Gap 1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Dash 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Gap 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Dash 3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Gap 3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Offset\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Taper\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\n\u00F8tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Taper Length Units\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Start Length\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper End Length\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper StartWidthPx\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper EndWidthPx\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Start Width\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper End Width\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Start Ease\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper End Ease\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Stroke Wave\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x06\x10tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Taper Wave Amount\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Wave Units\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Wavelength\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Wave Cycles\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Taper Wave Phase\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Transform Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\bltdgptdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vector Anchor\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00E2tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x0F\x00\x03\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Position\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00E2tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x0F\x00\x03\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Scale\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\"tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x02\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00P@Y\x00\x00\x00\x00\x00\x00@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\u00C0\u00DF@\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\u00DF@\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Skew\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\u00C0U@\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@U@\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Skew Axis\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Rotation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00DAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Group Opacity\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x01tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vector Materials Group\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00:\x16tdgptdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdmn\x00\x00\x00(ADBE Vec3D Front RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Front IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Bevel IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Side IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back RGB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x01\x12tdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x04\x00\x07\x00\x00\x00\x02\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00`@o\u00E0\x00\x00\x00\x00\x00@o\u00E0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Ambient\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Diffuse\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Specular\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@I\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Shininess\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Metal\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Reflection\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Gloss\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(@Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Fresnel\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back Xparency\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back XparRoll\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\x00\x01\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@Y\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Vec3D Back IOR\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LIST\x00\x00\x00\u00FAtdbstdsb\x00\x00\x00\x04\x00\x00\x00\x03tdsn\x00\x00\x00\x0EUtf8\x00\x00\x00\x06-_0_/-tdb4\x00\x00\x00|\u00DB\u0099\x00\x01\x00\x01\x00\x00\u00FF\u00FF\u00FF\u00FF\x00\x00x\x00?\x1A6\u00E2\u00EB\x1CC-?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cdat\x00\x00\x00(?\u00F0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdum\x00\x00\x00\b?\u00F0\x00\x00\x00\x00\x00\x00tduM\x00\x00\x00\b@\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00tdmn\x00\x00\x00(ADBE Group End\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<?xpacket begin=\"\u00EF\u00BB\u00BF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 6.0-c003 79.164527, 2020/10/15-17:48:32        \">\n   <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n      <rdf:Description rdf:about=\"\"\n            xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n            xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"\n            xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\"\n            xmlns:stEvt=\"http://ns.adobe.com/xap/1.0/sType/ResourceEvent#\">\n         <dc:format>application/vnd.adobe.aftereffects.preset-animation</dc:format>\n         <xmp:CreateDate>2022-10-18T11:57:12+02:00</xmp:CreateDate>\n         <xmp:MetadataDate>2022-10-18T11:57:12+02:00</xmp:MetadataDate>\n         <xmp:ModifyDate>2022-10-18T11:57:12+02:00</xmp:ModifyDate>\n         <xmpMM:InstanceID>xmp.iid:4cc606c5-542c-464f-b36e-056c3c1176da</xmpMM:InstanceID>\n         <xmpMM:DocumentID>xmp.did:4cc606c5-542c-464f-b36e-056c3c1176da</xmpMM:DocumentID>\n         <xmpMM:OriginalDocumentID>xmp.did:4cc606c5-542c-464f-b36e-056c3c1176da</xmpMM:OriginalDocumentID>\n         <xmpMM:History>\n            <rdf:Seq>\n               <rdf:li rdf:parseType=\"Resource\">\n                  <stEvt:action>created</stEvt:action>\n                  <stEvt:instanceID>xmp.iid:4cc606c5-542c-464f-b36e-056c3c1176da</stEvt:instanceID>\n                  <stEvt:when>2022-10-18T11:57:12+02:00</stEvt:when>\n               </rdf:li>\n            </rdf:Seq>\n         </xmpMM:History>\n      </rdf:Description>\n   </rdf:RDF>\n</x:xmpmeta>\n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                                                                                                    \n                           \n<?xpacket end=\"w\"?>", "preset_null.ffx", "presets" );
preset_null;
// ==================== |-------| ====================
// ==================== | 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",
    /**
     * A Custom layer type
     * Historically from duik
     */
    LAYER_TYPE: "duik.type"
}

/**
 * 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;
}

/**
 * Checks if this property has some keyframes
 * @param {Boolean} [recursive=true] If true and this is a group, checks all contained properties
 * @return {Boolean}
 */
DuAEProperty.prototype.hasKeys = function (recursive) {
    recursive = def(recursive, true);
    if (!recursive && this.isGroup()) return false;
    if (this.matchName == "ADBE Marker") return false;

    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY) {
        if (prop.numKeys > 0) return true;
    }
    else {
        for (var p = 1, num = this.numProperties(); p <= num; p++) {
            if (this.prop(p).hasKeys()) return true;
        }
    }

    return false;
}

/**
 * Checks if this property has some expressions
 * @param {Boolean} [recursive=true] If true and this is a group, checks all contained properties
 * @return {Boolean}
 */
DuAEProperty.prototype.hasExpressions = function (recursive) {
    recursive = def(recursive, true);
    if (!recursive && this.isGroup()) return false;

    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY) {
        if (prop.expression != "") return true;
    }
    else {
        for (var p = 1, num = this.numProperties(); p <= num; p++) {
            if (this.prop(p).hasExpressions()) return true;
        }
    }

    return false;
}

/**
 * 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.
 * @return {float} The speed
 */
DuAEProperty.prototype.speedAtTime = function(time, preExpression) {
    preExpression = def(preExpression, true);

    var prop = this.getProperty();

    if (prop.propertyType != PropertyType.PROPERTY) return 0;
    if (prop.numKeys == 0 && preExpression) return 0;

    var comp = this.comp;
    time = def(time, comp.time);

    var speed = DuMath.length(prop.valueAtTime(time + comp.frameDuration / 2, preExpression), prop.valueAtTime(time - comp.frameDuration / 2, preExpression));
    return speed / comp.frameDuration;
}

/**
 * Gets the velocity 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 velocity.
 * @return {float[]} The velocity
 */
DuAEProperty.prototype.velocityAtTime = function(time, preExpression) {
    preExpression = def(preExpression, true);

    var prop = this.getProperty();

    if (prop.propertyType != PropertyType.PROPERTY) return 0;
    if (prop.numKeys == 0 && preExpression) return 0;

    var comp = this.comp;
    time = def(time, comp.time);

    // For each axis
    var d = this.dimensions();
    var velocity = [];
    var nextValue = prop.valueAtTime(time + comp.frameDuration / 2, preExpression);
    var prevValue = prop.valueAtTime(time - comp.frameDuration / 2, preExpression);
    if (d == 1) velocity = (nextValue - prevValue) / comp.frameDuration;
    else {
        for (var i = 0; i < d; i++) {
            velocity.push((nextValue[i] - prevValue[i]) / comp.frameDuration);
        }
    }
    return velocity;
}

/**
 * Sets an expression to a property.<br />
 * With the ability to keep the initial value.
 * @param {string} expr - The expression
 * @param {bool} [keepValue=true] - When true, the method will try to keep the same resulting value as before applying the expression.
 */
DuAEProperty.prototype.setExpression = function(expr, keepValue) {
    keepValue = def(keepValue, true);

    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY) {
        if (!this.riggable()) return;
        var comp = this.comp;
        var originalValue = prop.valueAtTime(comp.time, false);

        //remove current expression
        if (keepValue) this.removeExpression();
        else try {
            prop.expression = ""
        } catch (e) {
            if (DuESF.debug) alert(e.description);
            return;
        }
        //set new expression
        try {
            prop.expression = expr;
        } catch (e) {
            if (DuESF.debug) alert(e.description);
        }

        //restore value
        if (this.editable() && keepValue && this.dimensions() > 0) this.setValue(2 * originalValue - prop.valueAtTime(comp.time, false));
    } else {
        for (var i = 1, n = prop.numProperties; i < n; i++) {
            p = new DuAEProperty(prop.property(i));
            p.setExpression(expr, keepValue);
        }
    }
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Replaces text in Expressions
 * @param {string} oldString - The string to replace
 * @param {string} newString - The new string
 * @param {boolean} [caseSensitive=true] - Whether the search has to be case sensitive
 */
DuAEProperty.prototype.replaceInExpressions = function(prop, oldString, newString, caseSensitive) {
    // Defaults
    caseSensitive = def(caseSensitive, true);

    // Run
    this.do(function(prop) {
        if (prop.riggable()) {
            prop = prop.getProperty();
            if (prop.expression = '') return;
            if (prop.expression.length < oldString.length) return;
            try {
                prop.expression = DuString.replace(prop.expression, oldString, newString, caseSensitive);
            } catch (e) {};
        }
    });
}

/**
 * Adds an expression to the property, linking it to the parent property
 * @param {DuAEProperty|Property} parentProp - The parent property.
 * @param {bool} [useThisComp] - Whether to begin the expression by 'thisComp' or 'comp("name")', default: will detect if the properties are in the same comp
 */
DuAEProperty.prototype.pickWhip = function(parentProp, useThisComp) {
    var childProp = this.getProperty();
    var parent = new DuAEProperty(parentProp);
    var parentProp = parent.getProperty();

    if (!this.riggable()) return;

    if (!isdef( useThisComp )) {
        var parentComp = parent.comp;
        var childComp = this.comp;
        if (parentComp.id == childComp.id) useThisComp = true;
        else useThisComp = false;
    }
    var exp = parent.expressionLink(useThisComp);
    this.setExpression(exp, false);
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Link all the properties found in this prop to all the same properties of parentProp (this is a recursive method)<br />
 * Note: any Property or PropertyGroup (and its subproperties) named "Data" will be linked the opposite way (from parentProp to childProp).
 * @memberof DuAEProperty
 * @param {PropertyBase|DuAEProperty} parentProp - The parent property
 * @param {bool} [useThisComp] - Whether to begin the expression by 'thisComp' or 'comp("name")', default: will detect if the properties are in the same comp
 * @param {LayerItem} [timeLayer=null] - A layer used to offset the time (typically, in case of link between precompositions, the precomposition layer).<br />
 * When not null, the start time of this layer will be taken into account to get the values and synchronize them.
 */
DuAEProperty.prototype.linkProperties = function(parentProp, useThisComp, timeLayer) {

    if (parentProp.name.toLowerCase() == 'data') return;

    var childProp = this.getProperty();
    var parent = new DuAEProperty(parentProp);
    var parentProp = parent.getProperty();
    if (childProp.matchName != parentProp.matchName) return;


    if (childProp.propertyType == PropertyType.PROPERTY && !childProp.elided && childProp.propertyValueType != PropertyValueType.NO_VALUE) {

        timeLayer = def(timeLayer, null);

        if (!isdef( useThisComp )) {
            var parentComp = parent.comp;
            var childComp = this.comp;
            useThisComp = parentComp.id == childComp.id;
        }

        //copy paste the animation / value
        var anim = parent.animation(false);
        if (anim != null) this.setAnim(anim, 0, true, true, false);

        // Expression
        var exp = [DuAEExpression.Id.LINK,
            'var link = ' + parent.expressionLink(useThisComp) + ';'
        ].join('\n');

        if (timeLayer != null) {
            exp += ['\nvar timeLayer = ' + DuAELayer.expressionLink(timeLayer) + ';',
                'var timeOffset = timeLayer.startTime;',
                'var result = link.valueAtTime(time + timeOffset);',
                'result;'
            ].join('\n');
        } else {
            exp += '\nvar result = link.value;\nresult;';
        }

        //set the link
        this.setExpression(exp, false);

    } else {
        if (parent.isEffect) {
            var insideData = 0;
            for (var p = 1, num = childProp.numProperties; p <= num; p++) {

                var subProp = parentProp(p);
                if (subProp.propertyValueType == PropertyValueType.NO_VALUE) {
                    if (subProp.name.toLowerCase() == 'data') {
                        insideData++;
                        continue;
                    }

                    if (insideData > 0) {
                        if (subProp.name == parentProp.name || subProp.name == "") insideData--;
                        else insideData++;
                        continue;
                    }
                }

                var child = new DuAEProperty(childProp(p));
                var sub = new DuAEProperty(subProp);

                if (insideData == 0) child.linkProperties(subProp, useThisComp, timeLayer);
                else sub.linkProperties(child, useThisComp, timeLayer);
            }
        } else {
            for (var p = 1, num = childProp.numProperties; p <= num; p++) {
                try {
                    var child = new DuAEProperty(childProp(p));
                    child.linkProperties(parentProp(p), useThisComp, timeLayer);
                } catch (e) {
                    DuDebug.throwError(childProp(p).name + ' could not be linked.', 'DuAEProperty.prototype.linkProperties', e, true);
                };
            }
        }
    }
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Removes all expressions found in groups or sections named "Data" in the property.
 */
DuAEProperty.prototype.removeDataExpressions = function(prop) {
    var prop = this.getProperty();

    if (prop.propertyType == PropertyType.PROPERTY && prop.name.toLowerCase() == 'data') {
        this.removeExpression();
    } else if (prop.isEffect) {
        var insideData = 0;
        for (var p = 1, num = prop.numProperties; p <= num; p++) {
            var subProp = prop(p);
            if (subProp.propertyValueType == PropertyValueType.NO_VALUE) {
                if (subProp.name.toLowerCase() == 'data') {
                    insideData++;
                    continue;
                }

                if (insideData > 0) {
                    if (subProp.name == "" || subProp.name == prop.name) insideData--;
                    else insideData++;
                    continue;
                }
            }
            if (insideData > 0) {
                new DuAEProperty(subProp).removeExpression();
            }
        }
    } else {
        if (prop.name.toLowerCase() == 'data') this.removeExpressions();
        for (var p = 1, num = this.numProperties(); p <= num; p++) {
            this.prop(p).removeDataExpressions();
        }
    }
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Removes all expressions found in the property.
 * @param {function} filter - A function which takes a string as a parameter (the expression). Returns true if the expression has to be removed.
 * @param {Boolean} [keepPostExpressionValue=true] Set to false to just remove the expressions and get back the pre expression value
 */
DuAEProperty.prototype.removeExpressions = function(filter, keepPostExpressionValue) {
    // Defaults
    keepPostExpressionValue = def(keepPostExpressionValue, true);

    // Run
    this.do(function(propInfo) {
        prop = propInfo.getProperty();
        var expression = prop.expression;

        if (expression == '') return;
        if (typeof filter === 'function') {
            if (!filter(expression)) return;
        }

        if (propInfo.riggable()) {
            if (keepPostExpressionValue) propInfo.setValue(prop.value);
            prop.expression = '';
        }
    });
}

/**
 * Alias for {@link DuAEProperty#removeExpressions DuAEProperty.removeExpressions()}
 * @alias DuAEProperty.removeExpressions()
 * @name removeExpression
 * @memberof DuAEProperty.prototype
 * @function
 */
DuAEProperty.prototype.removeExpression = DuAEProperty.prototype.removeExpressions;

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Recursilvely adds all the (supported) properties found to the essential graphics panel<br />
 * Note: any Property or PropertyGroup (and its subproperties) named "data" will be ignored.
 * @return {int} The number of properties added
 */
DuAEProperty.prototype.addToEGP = function() {
    var prop = this.getProperty();

    var numProps = 0;
    if (prop.name.toLowerCase() == 'data') return numProps;

    if (prop.propertyType == PropertyType.PROPERTY && prop.propertyValueType != PropertyValueType.NO_VALUE) {
        var comp = this.comp;
        var layer = this.layer;

        //set the link
        if (prop.canAddToMotionGraphicsTemplate(comp) && !prop.elided && prop.canSetExpression) {
            //get the name 
            var mPropName = layer.name + ' / ' + this.expressionLink(true, false);
            //add with name
            if (DuAE.version.version >= 16.1) {
                prop.addToMotionGraphicsTemplateAs(comp, mPropName);
            } else {
                prop.addToMotionGraphicsTemplate(comp);
            }

            //rename the master property if >=15.1 and < 16.1
            if (DuAE.version.version >= 15.1 && DuAE.version.version < 16.1) {
                var it = new DuList(comp.usedIn);
                it.do(function(mainComp) {
                    //search the layer of the precomp
                    for (var i = 1, num = mainComp.numLayers; i <= num; i++) {
                        var l = mainComp.layer(i);
                        if (l.source)
                            if (l.source.id == comp.id) {
                                l('ADBE Layer Overrides')(1).name = mPropName;
                            }
                    }
                });
            }
            numProps++;
        }
    } else {
        if (prop.isEffect) {
            var insideData = 0;
            for (var p = 1, num = prop.numProperties; p <= num; p++) {
                var subProp = prop(p);
                if (subProp.propertyValueType == PropertyValueType.NO_VALUE) {
                    if (subProp.name.toLowerCase() == 'data') {
                        insideData++;
                        continue;
                    }

                    if (insideData > 0) {
                        if (subProp.name == prop.name) insideData--;
                        else insideData++;
                        continue;
                    }
                }
                if (insideData == 0) numProps += new DuAEProperty(subProp).addToEGP();
            }
        } else {
            for (var p = 1, numP = prop.numProperties; p <= numP; p++) {
                numProps += new DuAEProperty(prop.property(p)).addToEGP();
            }
        }
    }
    return numProps;
}

/**
 * Checks if the property has an animation (keyframes)
 * @return {boolean} True if the property is animated
 */
DuAEProperty.prototype.animated = function() {
    var prop = this.getProperty();
    if (prop.canVaryOverTime) {
        if (prop.numKeys > 0) return true;
    }
    return false;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Gets the After Effects animated (with keyframes) properties in the propertyGroup
 * @param {PropertyType|PropertyValueType|string|function}	 [filter]	- A filter to get only a certain type, or value type, or property name or matchName.<br />
 * A function which take one PropertyBase as argument can be used to filter the properties: the Property will be returned if the function returns true.
 * @param {boolean}	[strict=false]	- If a string filter is provided, whether to search for the exact name/matchName or if it contains the filter.
 * @param {boolean}	[caseSensitive=true]	- If a string filter is provided, and not strict is false, does the search have to be case sensitive?
 * @return {DuAEProperty[]} The selected properties, an empty Array if nothing active or selected
 */
DuAEProperty.prototype.getAnimatedProps = function(filter, strict, caseSensitive) {
    var prop = this.getProperty();

    if (strict == undefined) strict = false;
    if (caseSensitive == undefined) caseSensitive = true;

    var props = [];

    if (!caseSensitive && typeof filter === "string") filter = filter.toLowerCase();

    if (prop.propertyType == PropertyType.PROPERTY && prop.matchName != 'ADBE Marker') {
        if (this.animated()) {
            if (!isdef( filter )) {
                props.push(prop);
            } else {
                var name = prop.name;
                var matchName = prop.matchName;
                if (!caseSensitive) {
                    name = name.toLowerCase();
                    matchName = matchName.toLowerCase();
                }

                if (strict && name === filter) props.push(prop);
                else if (strict && matchName === filter) props.push(prop);
                else if (typeof filter === 'string') {
                    if (name.indexOf(filter) >= 0) props.push(prop);
                    else if (matchName.indexOf(filter) >= 0) props.push(prop);
                } else if (prop.propertyValueType == filter) props.push(prop);
                else if (typeof filter === 'function') {
                    if (filter(prop)) props.push(prop);
                }
            }
        }
    } else {
        for (var i = 0, num = this.numProperties(); i < num; i++) {
            props = props.concat(this.prop(i + 1).getAnimatedProps(filter, strict, caseSensitive));
        }
    }

    return DuAE.getDuAEProperty(props);
}

/**
 * Gets the value range of the animated property.<br >
 * The property type must be one of: one D, two D, three D (spatial or not), Color.
 * If the property is not one of these types, returns an empty Array.
 * @param {int}	 [axis=0]	- The axis (or the color channel) to get the range
 * @param {bool}	 [preExpression=true]	- True to get the range from keyframes instead of the result of the exression
 * @param {bool}	 [fastMode=true]	- True to check the range with values only at keyframe times. False to check the range with all values, at each frame of the comp.
 * @return {float[]} The minimum and maximum value.<br />
 * The first item in the Array is not necesarily the lowest value, it is the first in time.
 */
DuAEProperty.prototype.range = function(axis, preExpression, fastMode) {
    axis = def(axis, 0);
    preExpression = def(preExpression, true);
    fastMode = def(fastMode, true);

    var prop = this.getProperty();

    if (!this.numerical()) return [];

    if (prop.expression == '' || !prop.expressionEnabled) preExpression = true;

    var comp = this.comp;
    var frames = comp.duration / comp.frameDuration;
    var min = prop.valueAtTime(0, preExpression);
    var minTime = 0;
    var max = prop.valueAtTime(0, preExpression);
    var maxTime = 0;
    var dimensions = this.dimensions();

    if (dimensions > 1) {
        max = max[axis];
        min = min[axis];
    }

    var count = frames - 1;
    if (fastMode && prop.numKeys > 1) count = prop.numKeys;
    else count = comp.duration / 4;

    if (count == 0) return [min, max];
    if (prop.numKeys < 2 && preExpression) return [min, max];

    for (var i = 0; i < count; i++) {
        var iTime = i * 0.25;
        if (fastMode && prop.numKeys > 1) iTime = prop.keyTime(i + 1);
        var val = prop.valueAtTime(iTime, preExpression);
        if (dimensions > 1) val = val[axis];
        if (val < min) {
            min = val;
            minTime = i;
        }
        if (val > max) {
            max = val;
            maxTime = i;
        }
    }

    if (minTime < maxTime) return [min, max];
    else return [max, min];
}

/**
 * Gets the maximum speed of the animated property.<br >
 * The property type must be one of: one D, two D, three D (spatial or not), Color.
 * If the property is not one of these types, returns 0.
 * @param {bool}	 [preExpression=true]	- True to get the velocity from keyframes instead of the result of the exression
 * @param {bool}	 [fastMode=true]	- True to limit the number of samples used to compute the velocity and make the process faster.<br />
 * The number of samples is automatically adapted from the duration of the composition.<br />
 * When true and if there are more than one keyframe, the velocity is sampled only between keyframes.
 * @return {float} The velocity.
 */
DuAEProperty.prototype.maxSpeed = function(preExpression, fastMode) {
    preExpression = def(preExpression, true);
    fastMode = def(fastMode, true);

    var prop = this.getProperty();

    var velocity = 0;
    if (!this.numerical()) return velocity;

    if (prop.expression == '') preExpression = true;

    var comp = this.comp;
    var frames = comp.duration / comp.frameDuration;

    var startFrame = 0;
    var endFrame = frames;
    var step = 1;
    if (fastMode) {
        if (prop.numKeys > 1) {
            startFrame = prop.keyTime(1) / comp.frameDuration;
            endFrame = prop.keyTime(prop.numKeys) / comp.frameDuration;
        }
    }

    var numFrames = endFrame - startFrame;

    if (numFrames > 1000 && fastMode) {
        step = Math.floor(numFrames / 500);
    }

    for (var i = startFrame; i < endFrame; i = i + step) {
        var vel = this.speedAtTime(i * comp.frameDuration, preExpression);
        if (vel > velocity) velocity = vel;
    }

    return velocity;
}

/**
 * Gets the maximum velocity of the animated property ofr a given axis.<br >
 * The property type must be one of: one D, two D, three D (spatial or not), Color.
 * If the property is not one of these types, returns 0.
 * @param {int}	 axis	- The axis
 * @param {bool}	 [preExpression=true]	- True to get the velocity from keyframes instead of the result of the exression
 * @param {bool}	 [fastMode=true]	- True to limit the number of samples used to compute the velocity and make the process faster.<br />
 * The number of samples is automatically adapted from the duration of the composition.<br />
 * When true and if there are more than one keyframe, the velocity is sampled only between keyframes.
 * @return {float} The velocity.
 */
DuAEProperty.prototype.maxVelocity = function( axis, preExpression, fastMode ) {
    preExpression = def(preExpression, true);
    fastMode = def(fastMode, true);

    var prop = this.getProperty();

    var velocity = null;
    if (!this.numerical()) return velocity;

    if (prop.expression == '') preExpression = true;

    var comp = this.comp;
    var frames = comp.duration / comp.frameDuration;

    var startFrame = 0;
    var endFrame = frames;
    var step = 1;
    if (fastMode) {
        if (prop.numKeys > 1) {
            startFrame = prop.keyTime(1) / comp.frameDuration;
            endFrame = prop.keyTime(prop.numKeys) / comp.frameDuration;
        }
    }

    var numFrames = endFrame - startFrame;

    if (numFrames > 1000 && fastMode) {
        step = Math.floor(numFrames / 500);
    }

    for (var i = startFrame; i < endFrame; i = i + step) {
        var vel = this.velocityAtTime(i * comp.frameDuration, preExpression);
        if (this.dimensions() > 1) {
            if (velocity == null) velocity = vel[axis];
            else if (vel[axis] > velocity) velocity = vel[axis];
        }
        else {
            if (velocity == null) velocity = vel;
            else if (vel > velocity) velocity = vel;
        }   
    }

    return velocity;
}

/**
 * Gets the minimum velocity of the animated property ofr a given axis.<br >
 * The property type must be one of: one D, two D, three D (spatial or not), Color.
 * If the property is not one of these types, returns 0.
 * @param {int}	 axis	- The axis
 * @param {bool}	 [preExpression=true]	- True to get the velocity from keyframes instead of the result of the exression
 * @param {bool}	 [fastMode=true]	- True to limit the number of samples used to compute the velocity and make the process faster.<br />
 * The number of samples is automatically adapted from the duration of the composition.<br />
 * When true and if there are more than one keyframe, the velocity is sampled only between keyframes.
 * @return {float} The velocity.
 */
DuAEProperty.prototype.minVelocity = function( axis, preExpression, fastMode ) {
    preExpression = def(preExpression, true);
    fastMode = def(fastMode, true);

    var prop = this.getProperty();

    var velocity = null;
    if (!this.numerical()) return velocity;

    if (prop.expression == '') preExpression = true;

    var comp = this.comp;
    var frames = comp.duration / comp.frameDuration;

    var startFrame = 0;
    var endFrame = frames;
    var step = 1;
    if (fastMode) {
        if (prop.numKeys > 1) {
            startFrame = prop.keyTime(1) / comp.frameDuration;
            endFrame = prop.keyTime(prop.numKeys) / comp.frameDuration;
        }
    }

    var numFrames = endFrame - startFrame;

    if (numFrames > 1000 && fastMode) {
        step = Math.floor(numFrames / 500);
    }

    for (var i = startFrame; i < endFrame; i = i + step) {
        var vel = this.velocityAtTime(i * comp.frameDuration, preExpression);
        if (this.dimensions() > 1) {
            if (velocity == null) velocity = vel[axis];
            else if (vel[axis] < velocity) velocity = vel[axis];
        }
        else {
            if (velocity == null) velocity = vel;
            else if (vel < velocity) velocity = vel;
        }   
    }

    return velocity;
}

/**
 * Scriptifies the given shape property.<br/>
 * Works only with path (bezier) properties.
 * @param {Boolean}	[offsetToCenter=false]	- If true, offset the path to the center
 * @param {string}	[varName=shape]	- A name for the variable storing the shape
 * @return {string} The scriptified shape
 */
DuAEProperty.prototype.scriptifyPath = function(offsetToCenter, varName) {
    var pathProperty = this.pathProperty();
    if (!pathProperty) return '';
    pathProperty = pathProperty.getProperty();

    offsetToCenter = def(offsetToCenter, false);
    varName = def(varName, 'shape');

    if (pathProperty.propertyType !== PropertyType.PROPERTY)
        DuDebug.throwError("Expected a shape property, got a group.", 'DuAEProperty.prototype.scriptifyPath');

    if (pathProperty.propertyValueType !== PropertyValueType.SHAPE)
        DuDebug.throwError("Expected a shape property, got another type of value.", 'DuAEProperty.prototype.scriptifyPath');

    offsetToCenter = def(offsetToCenter, false);

    var shape = pathProperty.value;
    var vertices = shape.vertices;

    if (offsetToCenter) {
        //get center and offset
        var sum = [0, 0];
        for (var i = 0; i < vertices.length; i++) {
            sum[0] += vertices[i][0];
            sum[1] += vertices[i][1];
        }
        var center = sum / vertices.length;
        //adjust values
        for (var i = 0; i < vertices.length; i++) {
            vertices[i][0] -= center[0];
            vertices[i][1] -= center[1];
        }
    }

    var verticesStr = vertices.toSource();
    var inTangentsStr = shape.inTangents.toSource();
    var outTangentsStr = shape.outTangents.toSource();
    var closedStr = shape.closed ? 'true' : 'false';

    var scriptified = [
        'var ' + varName + ' = new Shape();',
        varName + '.vertices = ' + verticesStr + ';',
        varName + '.inTangents = ' + inTangentsStr + ';',
        varName + '.outTangents = ' + outTangentsStr + ';',
        varName + '.closed = ' + closedStr + ';'
    ].join('\n');

    return scriptified;
}

/**
 * Export the (shape) property to the given file.
 * @example
 * var props = DuAEComp.getSelectedProps(PropertyValueType.SHAPE);
 * var prop = props[0].getProperty();
 * var out = prop.exportPathToJsxinc("D:/shape.test");
 * @param {String}	file	- The path or File where the jsxinc shape will be written
 * @param {Boolean}	[offsetToCenter=false]	- If true, offset the path to the center
 * @param {Boolean}	[append=false]	- If true, appends the shape at the end of the file instead of overwriting it.
 * @param {string}	[varName="shape"]	- A name for the variable storing the shape
 * @return {Boolean} Success
 */
DuAEProperty.prototype.exportPathToJsxinc = function(file, offsetToCenter, append, varName) {
    append = def(append, false);

    if (!(file instanceof File)) file = new File(file);

    var scriptPath = '';
    if (append) scriptPath += '\n';
    scriptPath += this.scriptifyPath(offsetToCenter, varName);

    return DuFile.write(
        file,
        scriptPath,
        append
    )
}

/**
 * Gets the vertices array in comp coordinates.<br/>
 * Works only with path (bezier) properties.
 * @return {float[][]} The vertices in comp coordinates.
 */
DuAEProperty.prototype.verticesToComp = function() {
    var pathProp = this.pathProperty();
    if (!pathProp) DuDebug.throwError("Expected a shape property, got another type of value.", 'DuAEProperty.prototype.verticesToComp');
    var pathProperty = pathProp.getProperty();

    //get the layer matrix
    var matrix = DuAEShapeLayer.getTransformMatrix(pathProp);

    //apply transform
    var vertices = [];
    var origin = pathProperty.value.vertices;
    for (var i = 0, num = origin.length; i < num; i++) {
        vertices.push(matrix.applyToPoint(origin[i]));
    }

    return vertices;
}

/**
 * Checks if the property is a bezier property, or return the child bezier property if this is a shape or a mask
 * @return {DuAEProperty|null} the bezier property or null if it is not.
 */
DuAEProperty.prototype.pathProperty = function() {
    var prop = this.getProperty();

    //get the path property in case it was a mask or a shape path selected
    if (prop.matchName == "ADBE Vector Shape - Group") {
        prop = prop.property("ADBE Vector Shape");
        return new DuAEProperty(prop);
    } else if (prop.matchName == "ADBE Mask Atom") {
        prop = prop.property("ADBE Mask Shape");
        return new DuAEProperty(prop);
    } else if (prop.propertyType !== PropertyType.PROPERTY) return null;
    if (prop.propertyValueType !== PropertyValueType.SHAPE) return null;

    return this;
}

/**
 * Gets the average speed of the proprety
 * @param {bool}	 [preExpression=true]	- True to get the velocity from keyframes instead of the result of the exression
 * @param {bool}	 [fastMode=true]	- True to limit the number of samples used to compute the velocity and make the process faster.
 * @return {float} The average speed in unit per second
 */
DuAEProperty.prototype.averageSpeed = function(preExpression, fastMode) {
    preExpression = def(preExpression, true);
    fastMode = def(fastMode, true);

    return DuAEProperty.getAverageSpeed([this], preExpression, fastMode);
}

/**
 * Adjust the value so it can be set on the specific property (adjust the number of dimensions or the type of value)
 * @param {Property|DuAEProperty} property The property
 * @param {any} value The value to set
 * @returns {any} The converted value
 */
DuAEProperty.prototype.fixValue = function(value) {

    prop = this.getProperty();

    var dimensions = this.dimensions();

    // Numeric
    if (dimensions > 0) {
        // Single
        if (dimensions == 1 && value instanceof Array) {
            value = value[0];
            return parseFloat(value);
        }
        // Array
        if (!(value instanceof Array)) value = [value];

        while (value.length < dimensions) {
            value.push(0);
        }
        while (value.length > dimensions) {
            value.pop();
        }

        return value;
    }

    // Non numeric

    var valueType = prop.propertyValueType;

    // Text
    if (valueType == PropertyValueType.TEXT_DOCUMENT) {
        var textDoc = prop.value;
        textDoc.text = value;
        return textDoc;
    }

    // TODO Path


    return value;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Quickly bakes an expression, adding a keyframe/frame
 * @param {float} [frameStep=1.0] By default, adds one keyframe per frame. Use a lower value to add sub-frame keyframes, a higher value to add less keyframes.
 */
DuAEProperty.prototype.quickBakeExpressions = function(frameStep) {
    // Defaults
    frameStep = def(frameStep, 1.0);
    range = [0, this.comp.duration];
    var step = this.comp.frameDuration * frameStep;
    // Run
    this.do(function(propInfo) {
        $.write("Baking expression:\nLayer: " + propInfo.layer.name + "\nProperty: " + propInfo.name);
        prop = propInfo.getProperty();
        // Checks
        if (!propInfo.riggable()) return;
        if (prop.expression == "") return;
        // We don't support all kind of properties yet
        var valueType = prop.propertyValueType;
        if (valueType == PropertyValueType.NO_VALUE) return;
        if (valueType == PropertyValueType.MARKER) return;
        if (valueType == PropertyValueType.LAYER_INDEX) return;
        if (valueType == PropertyValueType.MASK_INDEX) return;
        if (!propInfo.editable()) return;
        // Get the values and times
        var values = [];
        var times = [];
        for (var time = range[0]; time < range[1]; time += step) {
            times.push(time);
            values.push(prop.valueAtTime(time, false));
        }
        // Disable expression
        prop.expressionEnabled = false;
        // Remove current keyframes
        propInfo.removeAnimation();
        // Set values
        try { prop.setValuesAtTimes(times, values); }
        catch (e) {}
        // Clean
        propInfo.cleanKeyframes();
        // Set Continuous
        propInfo.setInterpolation("continuous");
    });
}

/**
 * Alias for {@link DuAEProperty#quickBakeExpressions DuAEProperty.quickBakeExpressions()}
 * @alias DuAEProperty.quickBakeExpressions()
 * @name quickBakeExpression
 * @memberof DuAEProperty.prototype
 * @function
 */
DuAEProperty.prototype.quickBakeExpression = DuAEProperty.prototype.quickBakeExpressions;

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Uses a smarter algorithm to bake the expression to keyframes
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 */
DuAEProperty.prototype.smartBakeExpressions = function(frameStep) {
    // Defaults
    frameStep = def(frameStep, 1.0);
    range = [0, this.comp.duration];
    var step = this.comp.frameDuration;
    // Run
    this.do(function(propInfo) {
        $.write("Baking expression:\nLayer: " + propInfo.layer.name + "\nProperty: " + propInfo.name);
        prop = propInfo.getProperty();
        // Checks
        if (!propInfo.riggable()) return;
        if (prop.expression == "") return;
        // We don't support all kind of properties yet
        var valueType = prop.propertyValueType;
        if (valueType == PropertyValueType.NO_VALUE) return;
        if (valueType == PropertyValueType.MARKER) return;
        if (valueType == PropertyValueType.LAYER_INDEX) return;
        if (valueType == PropertyValueType.MASK_INDEX) return;
        // Some properties can't be smart baked
        if (valueType == PropertyValueType.SHAPE) return propInfo.quickBakeExpressions();
        if (valueType == PropertyValueType.TEXT_DOCUMENT) return propInfo.quickBakeExpressions();
        if (valueType == PropertyValueType.CUSTOM_VALUE) return propInfo.quickBakeExpressions();

        // Check if we can add keyframes on the property
        if (!propInfo.editable()) return;
        // Get the values and times
        // The extremes
        var extremeValues = [];
        var extremeTimes = [];
        // The inflexions
        var inflexionValues = [];
        var inflexionSpeeds = [];
        var inflexionTimes = [];
        // First and last points if they're not extremes
        var startValue = null;
        var endValue = null;
        // Max amplitude of the values (to adjust precision later)
        var amplitude = 0;
        for (var time = range[0]; time <= range[1] + step; time += step) {
            var currentValue = prop.valueAtTime(time, false);
            var nextValue = prop.valueAtTime(time + step, false);
            var prevValue = prop.valueAtTime(time - step, false);
            var currentVelocity = propInfo.velocityAtTime(time, false);
            var nextVelocity = propInfo.velocityAtTime(time + step, false);
            var prevVelocity = propInfo.velocityAtTime(time - step, false);

            // Get values and times
            // Extremes
            if (DuMath.isExtremePoint(prevValue, currentValue, nextValue, Math.round(1/frameStep) )) {
                // Get amplitude
                for (var i = 0, n = extremeValues.length; i < n; i++) {
                    var length = DuMath.length(currentValue, extremeValues[i]);
                    if (length > amplitude) amplitude = length;
                }
                // Add to extremes
                extremeTimes.push(time);
                extremeValues.push(currentValue);
            }
            // Inflexions
            else if (DuMath.isInflexionPoint(prevVelocity, currentVelocity, nextVelocity)) {
                inflexionValues.push(currentValue);
                if (!(currentVelocity instanceof Array)) speed = currentVelocity;
                else if (prop.propertyValueType != PropertyValueType.TwoD && prop.propertyValueType != PropertyValueType.ThreeD) {
                    speed = propInfo.speedAtTime(time);
                } else speed = currentVelocity;
                inflexionSpeeds.push(speed);
                inflexionTimes.push(time);
            }
            // Start value
            else if (time == range[0]) startValue = currentValue;
            // End value
            else if (time >= range[1]) {
                endValue = currentValue;
                break;
            }
        }
        // Disable expression
        prop.expressionEnabled = false;
        // Remove current keyframes
        propInfo.removeAnimation();
        // Set extreme values
        if (extremeValues.length > 0) {
            // For some reason, there are still properties here which can't receive keyframes
            try {
                prop.setValuesAtTimes(extremeTimes, extremeValues);
                // Clean
                propInfo.cleanKeyframes();
                // Set to default Bézier
                propInfo.setInterpolation(KeyframeInterpolationType.BEZIER);
            }
            catch (e) {}
        }
        // Set inflexion values
        if (inflexionValues.length > 0) {
            // Adjust the precision for comparison
            var comparisonPrecision = amplitude / 10 * frameStep;
            // Set inflexion values and speeds
            for (var i = 0, n = inflexionValues.length; i < n; i++) {
                var inflexionTime = inflexionTimes[i];
                // Compare value to set the key frame
                var inflexionValue = inflexionValues[i];
                var currentValue = prop.valueAtTime(inflexionTime, true);
                // Already close to the current curve, no need to add a keyframe
                if (DuMath.length(inflexionValue, currentValue) <= comparisonPrecision) continue;
                // Set the new keyframe
                try {
                    prop.setValueAtTime(inflexionTime, inflexionValue);
                    var inflexionKey = prop.nearestKeyIndex(inflexionTime);
                    // Spatial, set to continuous
                    if (orop.isSpatial) propInfo.setKeyInterpolation(inflexionKey, 'continuous');
                    // other, set speed
                    else propInfo.setKeySpeed(inflexionKey, inflexionSpeeds[i]);
                }
                catch(e){}
            }
        }
        // Set start and end value
        if (startValue !== null) {
            // For some reason, there are still properties here which can't receive keyframes
            try {
                prop.setValueAtTime(range[0], startValue);
                var startKey = prop.nearestKeyIndex(range[0]);
                propInfo.setKeyInterpolation(startKey, 'continuous');
            }
            catch(e){}
        }
        if (endValue !== null) {
            // For some reason, there are still properties here which can't receive keyframes
            try {
                prop.setValueAtTime(range[1], endValue);
                var endKey = prop.nearestKeyIndex(range[1]);
                propInfo.setKeyInterpolation(endKey, 'continuous');
            }
            catch(e){}
        }
        // Clean
        propInfo.cleanKeyframes(Math.round(1/frameStep));
        propInfo.fixSpatialInterpolation(Math.round(1/frameStep));
    });
}

/**
 * Alias for {@link DuAEProperty#smartBakeExpressions DuAEProperty.smartBakeExpressions()}
 * @alias DuAEProperty.smartBakeExpressions()
 * @name smartBakeExpression
 * @memberof DuAEProperty.prototype
 * @function
 */
DuAEProperty.prototype.smartBakeExpression = DuAEProperty.prototype.smartBakeExpressions;

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Bakes the expressions to keyframes
 * @param {DuAEExpression.BakeAlgorithm} [mode=DuAEExpression.BakeAlgorithm.SMART] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 */
DuAEProperty.prototype.bakeExpressions = function(mode, frameStep) {
    mode = def(mode, DuAEExpression.BakeAlgorithm.SMART);
    frameStep = def(frameStep, 1.0);
    if (mode == DuAEExpression.BakeAlgorithm.SMART) this.smartBakeExpressions(frameStep);
    else this.quickBakeExpressions(frameStep);
}

/**
 * Alias for {@link DuAEProperty#bakeExpressions DuAEProperty.bakeExpressions()}
 * @alias DuAEProperty.bakeExpressions()
 * @name bakeExpression
 * @memberof DuAEProperty.prototype
 * @function
 */
DuAEProperty.prototype.bakeExpression = DuAEProperty.prototype.bakeExpressions;

/**
 * Finds the same property in the given comp (same path & name)
 * @param {CompItem} comp The composition where to find the property
 * @return {DuAEProperty|null} The property or null if it wasn't found
 */
DuAEProperty.prototype.findInComp = function(comp) {
    // Checks
    // We need to find the same layer.
    var layerIndex = this.layer.index;
    if (layerIndex > comp.numLayers) return null;

    // Go down the tree from the layer
    var parentProp = comp.layer(this.layer.index);
    for (var i = 0, n = this.parentIndices.length; i < n; i++) {
        // Make sure the property index exists...
        var propIndex = this.parentIndices[i];
        if (propIndex > parentProp.numProperties) return null;
        // Get the next prop
        parentProp = parentProp.property(propIndex);
    }

    // Check the matchname
    if (parentProp.matchName == this.matchName) return new DuAEProperty(parentProp);
    return null;
}

/**
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * Automatically sets all "transition" keyframes to roving, if the property is spatial.
 */
DuAEProperty.prototype.setRoving = function() {
    // Run
    var frameDuration = this.comp.frameDuration;

    this.do(function(propInfo) {
        var prop = propInfo.getProperty();
        // checks
        if (!prop.isSpatial) return;
        if (!prop.canVaryOverTime) return;

        // For each keyframe (except first and last one), check if the velocity just before or after is 0,
        // if not, set to roving
        for (var i = 2; i < prop.numKeys; i++)
        {
            var t = prop.keyTime(i);
            var cV = prop.keyValue(i);
            var pV = prop.valueAtTime(t - frameDuration, true);
            if (DuMath.equals(pV, cV)) continue;
            var nV = prop.valueAtTime(t + frameDuration, true);
            if (DuMath.equals(nV, cV)) continue;
            prop.setRovingAtKey(i, true);
        }
    });
}

/**
 * Snaps keyframes to the closest frames if they're in between.
 * <p><i><strong>Recursive</strong>: this method can run on a property group.</i></p>
 * @param {int[]} [keys] An optional list of key indices to snap (could be <code>DuAEProperty.selectedKeys()</code> for example). If omitted, will snap all keyframes.
 */
DuAEProperty.prototype.snapKeys = function(keys) {
    this.do(function(prop) {
        var ks = [];
        if (!isdef( keys )) {
            for (var i = 1, n = prop.numKeys(false); i <= n; i++ )
                ks.push(i);
        }
        else ks = keys;

        var frameDuration = prop.comp.frameDuration;
        var halfFrame = frameDuration / 2.0;
        // Snap each key
        for (var k = 0; k < ks.length; k++) {
            var key = prop.keyAtIndex(ks[k]);
            var mod = key._time % frameDuration;
            if (mod != 0 ) {
                if (mod < halfFrame) key._time -= mod;
                else key._time += frameDuration - mod;
                prop.removeKey(key);
                prop.setKey(key, 0);
            }
        }
    });
}

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


/**
 * Gets the After Effects properties in the property
 * @static
 * @param {PropertyBase|DuAEProperty}	property	- The layer
 * @param {PropertyType|PropertyValueType|string|function}	 [filter]	- A filter to get only a certain type, or value type, or property name or matchName.<br />
 * A function which take one PropertyBase as argument can be used to filter the properties: the Property will be returned if the function returns true.
 * @param {boolean}	[strict=false]	- If a string filter is provided, whether to search for the exact name/matchName or if it contains the filter.
 * @param {boolean}	[caseSensitive=true]	- If a string filter is provided, and not strict is false, does the search have to be case sensitive?
 * @return {DuAEProperty[]} The selected properties, an empty Array if nothing found
 */
DuAEProperty.getProps = function(property, filter, strict, caseSensitive) {
    if (strict == undefined) strict = false;
    if (caseSensitive == undefined) caseSensitive = true;
    var prop;
    if (property instanceof DuAEProperty) prop = property.getProperty();
    else prop = property;

    var props = [];

    if (!caseSensitive && typeof filter === "string") filter = filter.toLowerCase();

    var name = prop.name;
    var matchName = prop.matchName;
    if (!caseSensitive) {
        name = name.toLowerCase();
        matchName = matchName.toLowerCase();
    }

    if (strict && name === filter) props.push(prop);
    else if (strict && matchName === filter) props.push(prop);
    else if (!strict && typeof filter === "string") {
        if (name.indexOf(filter) >= 0) props.push(prop);
        else if (matchName.indexOf(filter) >= 0) props.push(prop);
    } else if (typeof filter === "function") {
        if (filter(prop)) props.push(prop);
    } else if (prop.propertyType == filter) {
        props.push(prop);
    } else if (prop.propertyType == PropertyType.PROPERTY) {
        if (prop.propertyValueType == filter) props.push(prop);
    }


    if (prop.numProperties > 0) {
        for (var k = 1, numP = prop.numProperties; k <= numP; k++) {
            props = props.concat(DuAEProperty.getProps(prop.property(k), filter, strict, caseSensitive));
        }
    }

    return DuAE.getDuAEProperty(props);
}

/**
 * Generates a new unique name for a marker for this marker porperty
 * @static
 * @param {string} newName	- The wanted new name
 * @param {Property} markerProp 	- The marker property
 * @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.
 */
DuAEProperty.newUniqueMarkerName = function(newName, markerProp, increment) {
    if (increment == undefined) increment = true;
    var markerNames = [];
    for (var i = 1, num = prop.numKeys; i <= num; i++) {
        markerNames.push(prop.keyValue(i).comment);
    }
    return DuString.generateUnique(newName, markerNames, increment);
}

/**
 * Changes the interpolation type on selected keyframes, or sets a new key at current time if there are no keyframes selected.
 * @static
 * @param {Layer[]|LayerCollection} layers - The layers containing the properties
 * @param {PropertyBase[]|DuAEProperty[]} props - The properties
 * @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.setInterpolationType = function(layers, props, typeIn, typeOut, easeInValue, easeOutValue) {
    typeOut = def(typeOut, typeIn);
    easeInValue = def(easeInValue, 33);
    if (isNaN(easeInValue)) easeInValue = 33;
    easeOutValue = def(easeOutValue, easeInValue);
    if (isNaN(easeOutValue)) easeOutValue = 33;

    if (layers.length == 0) return;

    if (!DuAELayer.haveSelectedKeys(layers)) {
        DuAEProperty.addKey(props, typeIn, typeOut, easeInValue, easeOutValue);
    } else {
        for (var i = 0; i < props.length; i++) {
            var aeprop = new DuAEProperty(props[i]);
            var prop = aeprop.getProperty();

            if (prop.canVaryOverTime) {
                //for keys
                for (var k = 0, num = prop.selectedKeys.length; k < num; k++) {
                    aeprop.setKeyInterpolation(prop.selectedKeys[k], typeIn, typeOut, easeInValue, easeOutValue);
                }
            }
        }
    }
}

/**
 * Gets the maximum speed of the animated properties
 * @static
 * @param {Property[]|DuAEProperty[]} props - The Properties
 * @param {bool}	 [preExpression=true]	- True to get the velocity from keyframes instead of the result of the exression
 * @return {float} The average speed
 */
DuAEProperty.getMaximumSpeed = function(props) {
    var maxSpeed = 0;

    for (var i = 0; i < props.length; i++) {
        var prop = new DuAEProperty(prop);
        var speed = prop.maxVelocity(false);
        if (speed > maxSpeed) maxSpeed = speed;
    }

    return maxSpeed;
}

/**
 * Locks the properties with an expression so thier values cannot be changed
 * @param {PropertyBase|DuAEProperty|PropertyBase[]|DuAEProperty[]} properties - The property or properties
 */
DuAEProperty.lock = function(properties) {
    var it = new DuList(properties);
    it.do(function(property) {
        property = new DuAEProperty(property);
        var p = property.getProperty();

        if (property.riggable()) {
            var dimensions = property.dimensions();
            var exp = "";
            if (dimensions == 0) exp = "value";
            else if (dimensions == 1) exp = p.value.toString();
            else exp = p.value.toSource();
            property.setExpression(exp);
        }
    });
}

/**
 * Gets the average speed of the animated propreties
 * @static
 * @param {Property[]|DuAEProperty[]|DuList.<DuAEProperty>|DuList.<Property>} props - The Properties
 * @param {bool}	 [preExpression=true]	- True to get the velocity from keyframes instead of the result of the exression
 * @param {bool}	 [fastMode=true]	- True to limit the number of samples used to compute the velocity and make the process faster.
 * @return {float} The average speed in unit per second
 */
DuAEProperty.getAverageSpeed = function(props, preExpression, fastMode) {
    preExpression = def(preExpression, true);
    fastMode = def(fastMode, true);

    props = new DuList(props);

    var averageSpeed = 0;
    var count = 0;

    for (var i = 0; i < props.length(); i++) {
        var aeprop = new DuAEProperty(props.at(i));
        var prop = aeprop.getProperty();

        if (prop.propertyType != PropertyType.PROPERTY) continue;
        if (!prop.canVaryOverTime) continue;
        if (prop.numKeys < 1 && !preExpression) continue;

        var comp = aeprop.comp;
        var frames = comp.duration / comp.frameDuration;
        var lastTime = comp.duration;
        var firstTime = 0;
        if (preExpression || (fastMode && prop.numKeys > 1)) {
            lastTime = prop.keyTime(prop.numKeys);
            firstTime = prop.keyTime(1);
        }
        var lastFrame = lastTime / comp.frameDuration;
        var firstFrame = firstTime / comp.frameDuration;
        if (lastFrame > frames) lastFrames = frames;
        if (firstFrame < 1) firstFrame = 1;

        var step = 1;
        if (fastMode) {
            var numFrames = lastFrame - firstFrame;
            if (numFrames > 1000) {
                step = Math.floor(numFrames / 500);
            }
        }

        var sum = 0;
        for (var frame = firstFrame; frame < lastFrame; frame = frame + step) {
            var time = frame * comp.frameDuration;
            sum += aeprop.speedAtTime(time, preExpression);
        }
        var speed = sum / (lastFrame - firstFrame);

        if (speed > 0) {
            averageSpeed += speed;
            count++;
        }
    }

    averageSpeed = averageSpeed / count;
    return averageSpeed;
}

/**
 * Makes a horizontal symetry transformation on the paths, using the same axis of symetry for all shapes (shapes must be on the same layer).
 * @param {Property[]|DuAEProperty[]}	pathProperties	- The After Effects Properties containing the paths to symetrize
 */
DuAEProperty.pathHorizontalSymetry = function(pathProperties) {
    if (!(pathProperties instanceof Array)) pathProperties = [pathProperties];

    var shapes = [];
    //get shapes and center
    var center = 0;
    var verticesCount = 0;
    for (var i = 0; i < pathProperties.length; i++) {
        var prop = pathProperties[i];
        if (prop instanceof DuAEProperty) prop = prop.getProperty();
        var shape = prop.value;
        shapes.push(shape);
        for (var j = 0; j < shape.vertices.length; j++) {
            verticesCount++;
            center += shape.vertices[j][0];
        }
    }
    center = center / verticesCount;
    center = center * 2;

    //compute
    for (var i = 0; i < shapes.length; i++) {
        var aeprop = new DuAEProperty(pathProperties[i]);
        var prop = aeprop.getProperty();

        var shape = shapes[i];
        var vertices = shape.vertices;
        var inTangents = shape.inTangents;
        var outTangents = shape.outTangents;
        for (var j = 0; j < shape.vertices.length; j++) {
            vertices[j][0] = center - vertices[j][0];
            inTangents[j][0] = -inTangents[j][0];
            outTangents[j][0] = -outTangents[j][0];
        }
        shape.vertices = vertices;
        shape.inTangents = inTangents;
        shape.outTangents = outTangents;
        if (prop.numKeys > 0) {
            prop.setValueAtTime(aeprop.comp().time, shape);
        } else {
            prop.setValue(shape);
        }
    }
}

/**
 * Makes a vertical symetry transformation on the paths, using the same axis of symetry for all shapes (shapes must be on the same layer).
 * @param {Property[]}	pathProperties	- The After Effects Properties containing the paths to symetrize
 */
DuAEProperty.pathVerticalSymetry = function(pathProperties) {
    if (!(pathProperties instanceof Array)) pathProperties = [pathProperties];

    var shapes = [];
    //get shapes and center
    var center = 0;
    var verticesCount = 0;
    for (var i = 0; i < pathProperties.length; i++) {
        var prop = pathProperties[i];
        if (prop instanceof DuAEProperty) prop = prop.getProperty();
        var shape = prop.value;
        shapes.push(shape);
        for (var j = 0; j < shape.vertices.length; j++) {
            verticesCount++;
            center += shape.vertices[j][1];
        }
    }
    center = center / verticesCount;
    center = center * 2;

    //compute
    for (var i = 0; i < shapes.length; i++) {
        var aeprop = new DuAEProperty(pathProperties[i]);
        var prop = pathProperties[i];

        var shape = shapes[i];
        var vertices = shape.vertices;
        var inTangents = shape.inTangents;
        var outTangents = shape.outTangents;
        for (var j = 0; j < shape.vertices.length; j++) {
            vertices[j][1] = center - vertices[j][1];
            inTangents[j][1] = -inTangents[j][1];
            outTangents[j][1] = -outTangents[j][1];
        }
        shape.vertices = vertices;
        shape.inTangents = inTangents;
        shape.outTangents = outTangents;
        if (prop.numKeys > 0) {
            prop.setValueAtTime(aeprop.comp().time, shape);
        } else {
            prop.setValue(shape);
        }
    }
}

/**
 * Gets the sourceRect of the properties (their bounds) in layer coordinates
 * @param {Property[]}	pathProperties	- The After Effects Properties containing the paths
 * @param {Boolean}	[includeTangents=false]	Wether to include tangents in the bounds or not
 * @return {float[]} The bounds [top, left, width, height]
 */
DuAEProperty.pathBounds = function(pathProperties, includeTangents) {
    if (!(pathProperties instanceof Array)) pathProperties = [pathProperties];

    includeTangents = def(includeTangents, false);

    var top = 32000;
    var left = 32000;
    var width = 0;
    var height = 0;

    var verts = [];
    var inTs = [];
    var outTs = [];

    for (var i = 0, n = pathProperties.length; i < n; i++) {
        var prop = new DuAEProperty(pathProperties[i]);
        var pathProp = prop.pathProperty().getProperty();
        if (!pathProp) continue;
        var shape = pathProp.value;
        var vert = shape.vertices;
        var inT = shape.inTangents;
        var outT = shape.outTangents;

        verts = verts.concat(vert);
        inTs = inTs.concat(inT);
        outTs = outTs.concat(outT);
    }

    var sBounds = DuMath.bounds(verts);
    if (sBounds[0] < left) left = sBounds[0];
    if (sBounds[1] < top) top = sBounds[1];
    var w = sBounds[2] - left;
    if (w > width) width = w;
    var h = sBounds[3] - top;
    if (h > height) height = h;

    if (includeTangents) {
        // Adjust coordinates because tangents are given relative to their vertex
        for (var j = 0, nj = vert.length; j < nj; j++) {
            inTs[j] = inTs[j] + verts[j];
            outTs[j] = outTs[j] + verts[j];
        }

        var iBounds = DuMath.bounds(inTs);
        if (iBounds[0] < left) left = iBounds[0];
        if (iBounds[1] < top) top = iBounds[1];
        var w = iBounds[2] - left;
        if (w > width) width = w;
        var h = iBounds[3] - top;
        if (h > height) height = h;

        var oBounds = DuMath.bounds(outTs);
        if (oBounds[0] < left) left = oBounds[0];
        if (oBounds[1] < top) top = oBounds[1];
        var w = oBounds[2] - left;
        if (w > width) width = w;
        var h = oBounds[3] - top;
        if (h > height) height = h;
    }

    return [top, left, width, height];
}

/**
 * Checks if the property contains a Bézier "path" property (it's a mask path or a shape layer path).<br />
 * The function can be used as a filter for {@link DuAELayer.getSelectedProps}.
 * @param {PropertyGroup} prop The property to check
 * @returns {Boolean} true if it's a path property.
 */
DuAEProperty.isPathProperty = function(prop) {
    if (prop.matchName == "ADBE Vector Shape - Group") return true;
    if (prop.matchName == "ADBE Mask Atom") return true;
    return false;
}

/**
 * Safely renames a property (without breaking expressions)
 * @param {PropertyBase} prop The property
 * @param {string} name The new name.
 * @returns {string} The new name.
 */
DuAEProperty.rename = function(prop, name) {
    var oldName = prop.name;
    prop.name = name;
    var newName = prop.name;
    app.project.autoFixExpressions(oldName, newName);
}

/**
 * Compares two shape values
 * @param {Shape} shape1
 * @param {Shape} shape2
 * @return {bool} true if they're the same
 */
DuAEProperty.shapeValueEquals = function( shape1, shape2, precision ) {
    precision = def(precision, 1);

    if (shape1.closed != shape2.closed) return false;

    if (!DuMath.equals(shape1.vertices, shape2.vertices, precision, true)) return false;
    if (!DuMath.equals(shape1.inTangents, shape2.inTangents, precision)) return false;
    if (!DuMath.equals(shape1.outTangents, shape2.outTangents, precision)) return false;
    return true;
}

/**
 * Compares two text values
 * @param {TextDocument} text1
 * @param {TextDocument} text2
 * @return {bool} true if they're the same
 */
DuAEProperty.textValueEquals = function( text1, text2 ) {
    return text1.text == text2.text;
}// ==================== |------------| ====================
// ==================== | expression | ====================
// ==================== |------------| ====================

/**
 * After Effects expression tools
 * @namespace
 * @category DuAEF
 */
var DuAEExpression = {};

//caches
//this must be set to true during work on the cache to prevent it from updating
DuAEExpression.suspendCacheUpdates = false;

// the expression cache is an array of DuAEPropertyExpression
DuAEExpression.cache = [];
DuAEExpression.lastCacheUpdateTime = 0;

/**
 * The different modes available to bake expressions
 * @enum {int}
 * @readonly
 */
DuAEExpression.BakeAlgorithm = {
    SMART: 0,
    PRECISE: 1
}

/**
 * The list of expression IDs, added at the beginning of generated expressions.
 * @enum {string}
 */
DuAEExpression.Id = {
    LINK: "/*== DuAEF: property link ==*/"
}

/**
 * Updates the cache of the expressions used by Duik to speed up batch process of expressions in the whole project.<br />
 * It's automatically run when needed if it's not been updated in a long time (1 mn) or if it's empty
 * @param {bool} [selectionMode=DuAE.SelectionMode.ALL_COMPOSITIONS] What to update
 */
DuAEExpression.updateCache = function(selectionMode) {

    if (DuAEExpression.suspendCacheUpdates) return;

    selectionMode = def(selectionMode, DuAE.SelectionMode.ALL_COMPOSITIONS);

    //clear cache
    DuAEExpression.cache = [];

    var comps = [];
    if (selectionMode == DuAE.SelectionMode.ACTIVE_COMPOSITION || selectionMode == DuAE.SelectionMode.SELECTED_LAYERS || selectionMode == DuAE.SelectionMode.SELECTED_PROPERTIES) comps = [DuAEProject.getActiveComp()];
    else if (selectionMode == DuAE.SelectionMode.SELECTED_COMPOSITIONS) comps = app.project.selection;
    else if (selectionMode == DuAE.SelectionMode.ALL_COMPOSITIONS) comps = DuAEProject.getComps();

    for (var i = 0, num = comps.length; i < num; i++) {
        var comp = comps[i];
        if (!(comp instanceof CompItem)) continue;

        var layers = comp.layers;
        if (selectionMode == DuAE.SelectionMode.SELECTED_LAYERS) layers = comp.selectedLayers;
        else if (selectionMode == DuAE.SelectionMode.SELECTED_PROPERTIES) layers = DuAEComp.getSelectedProps();

        new DuList(layers).do(function(layer) {
            layer = new DuAEProperty(layer);
            layer.addToExpressionCache();
        });
    }

    DuAEExpression.lastCacheUpdateTime = new Date().getTime();
}

/**
 * Runs a function on all expressions
 * @param {function} func The function to run, which takes one param, a {@link DuAEPropertyExpression} object.
 * @param {bool} [selectionMode=false] What to update
 * @param {boolean} [updateCache=true] When false, the cache won't be updated before running the function. Set this to false if you already have updated the cache to improve performance.
 * @param {boolean} [apply=true] When false, the cache won't be applied back to Ae. Set this to false if you need to run other methods on expressions before applying the result to improve performance.
 */
DuAEExpression.doInExpresssions = function(func, selectionMode, updateCache, apply) {
    selectionMode = def(selectionMode, DuAE.SelectionMode.ALL_COMPOSITIONS);
    updateCache = def(updateCache, true);
    apply = def(apply, true);

    if (updateCache) DuAEExpression.updateCache(selectionMode);

    for (var i = 0, num = DuAEExpression.cache.length; i < num; i++) {
        func(DuAEExpression.cache[i]);
    }

    if (apply) DuAEExpression.applyCache();
}

/**
 * Applies all the expressions stored in the cache to the actual properties in After Effects, if and only if they've been modified.
 * @param {DuAEPropertyExpression[]} [cache] The cache to apply, if different from the automatic DuAEF Cache
 */
DuAEExpression.applyCache = function( cache ) {
    cache = def(cache, DuAEExpression.cache);
    for (var i = 0, num = DuAEExpression.cache.length; i < num; i++) {
        DuAEExpression.cache[i].apply();
    }
}

/**
 * Converts the expression as a string which can be copy/pasted and included in a script.
 * @param {Property|DuAEProperty|string} prop - The property containing the expression or the expression itself.
 * @param {string} [varName] - A name for the variable
 * @return {string} The stringified expression.
 */
DuAEExpression.scriptifyExpression = function(prop, varName) {
    varName = def(varName, '');
    varName = DuString.toCamelCase(varName);

    function line(str) {
        return "'" + str.replace("\r", "").replace(/'/g, "\\'") + "'";
    }

    var exp = '';
    if (jstype(prop) === 'string') exp = prop;
    else {
        if (prop instanceof DuAEProperty) prop = prop.getProperty();
        exp = prop.expression;
    }

    var expArray = exp.split('\n');
    var expString = "";
    if (varName != '') expString += "var " + varName + " = ";

    expString += "[" + line(expArray[0]);

    for (var i = 1; i < expArray.length; i++) {
        expString += ",\n\t" + line(expArray[i]);
    }
    expString += "\n\t].join('\\n');";

    return expString;
}

/**
 * The expression library<br />
 * Use {@link DuAEExpression.Library.get} and {@link DuAEExpression.Library.getRequirements}<br />
 * to easily include the methods and classes listed here to your expressions.<br />
 * These methods take the name (listed here) of the function/class as arguments.
 * @namespace
 * @memberof DuAEExpression
 * @category DuAEF
 */
DuAEExpression.Library = {};

/**
 * Gets functions and their dependencies from the library.
 * @param {string[]} functions The name of the functions to get
 * @return {string} The expression
 */
DuAEExpression.Library.get = function(functions) {
    var exp = functions;
    new DuList(functions).do(function(functionName) {
        var r = DuAEExpression.Library.getRequirements(functionName);
        exp = exp.concat(r);
    });

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

    var expString = "/*\n" +
        "=== The following code uses DuAEF, the Duduf After Effects Framework ===\n\n" +
        "   Copyright (c) 2008 - 2022 Nicolas Dufresne, RxLaboratory, and contributors\n\n" +
        "   This code is free software: you can redistribute it and/or modify\n" +
		"   it under the terms of the GNU General Public License as published by\n" +
		"   the Free Software Foundation, either version 3 of the License, or\n" +
		"   (at your option) any later version.\n" +
        "*/\n\n";

    exp.do(function(expName) {
        expString += DuAEExpression.Library[expName].expression + '\n';
    });

    return expString;
}

/**
 * A recursive method to get all the requirements (dependencies) of a function from a library
 * @param {string} functionName The name of the function
 * @return {string[]} The names of the required functions, including the querried one
 */
DuAEExpression.Library.getRequirements = function(functionName) {
    var r = DuAEExpression.Library[functionName].requirements;
    if (r.length > 0) {
        for (var i = 0, iN = r.length; i < iN; i++) {
            r = r.concat(DuAEExpression.Library.getRequirements(r[i]));
        }
    }

    return r;
}// ==================== |--------------------| ====================
// ==================== | expression_library | ====================
// ==================== |--------------------| ====================

/* License
	DuExpression Library

	Copyright (c) 2020-2022 Nicolas Dufresne and contributors

	This file is part of DuExpression - The Duduf Expression Library.

		DuExpression 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.

		DuExpression 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 DuExpression. If not, see <http://www.gnu.org/licenses/>.
*/

/*
    This script can be included in any other script and can be used to easily include Functions from the Duduf Expression Library into your own expressions.
    It is also available in DuAEF, along with some useful functions to help you include requirements when adding a function to your expressions.
*/

if (typeof DuAEExpression === "undefined") DuAEExpression = {};

/**
* Expression Library<br />
* Using DuAEF, you can easily include the methods and classes listed here to your expressions,<br />
* using {@link DuAEExpression.Library.get} and {@link DuAEExpression.Library.getRequirements}. These methods take the name of the function/class as arguments.
* @namespace
* @name Library
* @memberof DuAEExpression
* @category DuAEF
*/
if (typeof DuAEExpression.Library === "undefined") DuAEExpression.Library = [];
/**
  * Fuzzy Logics for expressions. See {@link https://github.com/Nico-Duduf/DuFuzzyLogic} for more explanations
  * @class
  * @classdesc Fuzzy Logics for expressions. See {@link https://github.com/Nico-Duduf/DuFuzzyLogic} for more explanations
  * @author Nicolas "Duduf" Dufresne
  * @license GPL-v3
  * @copyright 2020-2022 Nicolas Dufresne and contributors
  * @requires gaussian
  * @requires inverseGaussian
  * @requires inverseLogistic
  * @requires logistic
  * @requires mean
  * @category ExpressionLibrary
*/
DuAEExpression.Library["FuzzyLogic"] = {};
DuAEExpression.Library["FuzzyLogic"].expression = ['function FuzzySet( name, valueNot, valueIS, shape, shapeAbove, plateauMin, plateauMax)',
	'{',
	'var min;',
	'var max;',
	'if (valueNot > valueIS){',
	'max = valueNot;',
	'min = valueNot - (valueNot - valueIS) * 2;',
	'}',
	'else',
	'{',
	'min = valueNot;',
	'max = valueNot + (valueIS - valueNot) * 2;',
	'}',
	'if (typeof shape === "undefined") shape = "linear";',
	'if (typeof shapeAbove === "undefined") shapeAbove = shape;',
	'if (typeof plateauMin === "undefined") plateauMin = mean([min, max]);',
	'if (typeof plateauMax === "undefined") plateauMax = mean([min, max]);',
	'this.min = min;',
	'this.max = max;',
	'this.shapeIn = shape;',
	'this.shapeOut = shapeAbove;',
	'this.plateauMin = plateauMin;',
	'this.plateauMax = plateauMax;',
	'this.name = name;',
	'}',
	'FuzzySet.prototype = {',
	'contains: function ( v, quantifier )',
	'{',
	'var val;',
	'if (v instanceof FuzzyValue) val = v.crispify(false);',
	'else val = v;',
	'quantifier = getQuantifier(quantifier);',
	'if (val >= this.plateauMin && val <= this.plateauMax)',
	'{',
	'return quantifier(1);',
	'}',
	'else if (val < this.plateauMin)',
	'{',
	'if (this.shapeIn === "constant")',
	'{',
	'return quantifier(1);',
	'}',
	'else if (this.shapeIn === "square")',
	'{',
	'var min = mean(this.plateauMin, this.min);',
	'if (val >= min) return quantifier(1);',
	'else return quantifier(0);',
	'}',
	'else if (this.shapeIn === "linear")',
	'{',
	'if (val < this.min) return quantifier(0);',
	'else return quantifier( (val-this.min) / (this.plateauMin - this.min) );',
	'}',
	'else if (this.shapeIn === "sigmoid")',
	'{',
	'var mid = (this.plateauMin + this.min) / 2;',
	'var rate = 6 / (this.plateauMin - this.min);',
	'return quantifier(logistic(val, mid, 0, 1, rate));',
	'}',
	'else if (this.shapeIn === "gaussian")',
	'{',
	'var width = this.plateauMin - this.min;',
	'return quantifier( gaussian( val, 0, 1, this.plateauMin, width));',
	'}',
	'else return quantifier(0);',
	'}',
	'else',
	'{',
	'if (this.shapeOut === "constant")',
	'{',
	'return quantifier(1);',
	'}',
	'else if (this.shapeOut === "square")',
	'{',
	'var max = mean(this.plateauMax, this.max);',
	'if (val <= max) return quantifier(1);',
	'else return quantifier(0);',
	'}',
	'else if (this.shapeOut === "linear")',
	'{',
	'if (val > this.max) return quantifier(0);',
	'else return quantifier (1 - ((val - this.plateauMax ) / (this.max - this.plateauMax) ));',
	'}',
	'else if (this.shapeOut === "sigmoid")',
	'{',
	'var mid = (this.plateauMax + this.max) / 2;',
	'var rate = 6 / (this.max - this.plateauMax);',
	'return quantifier( 1 - logistic(val, mid, 0, 1, rate));',
	'}',
	'else if (this.shapeOut === "gaussian")',
	'{',
	'var width = this.max - this.plateauMax;',
	'return quantifier( gaussian( val, 0, 1, this.plateauMax, width) );',
	'}',
	'else return quantifier(0);',
	'}',
	'},',
	'getValues: function ( veracity )',
	'{',
	'if (typeof veracity === "undefined") veracity = 0.5;',
	'if (veracity instanceof FuzzyVeracity) veracity = veracity.veracity;',
	'var defaultValue = mean( [this.plateauMin, this.plateauMax] );',
	'if ( this.shapeIn === "constant" && this.shapeOut === "constant" ) return [ this.min, this.plateauMin, defaultValue, this.plateauMax, this.max];',
	'var crisp = [];',
	'if (veracity >= 1) crisp = [this.plateauMin, defaultValue, this.plateauMax];',
	'if (this.shapeIn === "constant" && veracity == 1)',
	'{',
	'crisp.push(this.min);',
	'}',
	'else if (this.shapeIn === "square")',
	'{',
	'if (veracity >= 0.5) crisp.push( this.plateauMin );',
	'else crisp.push( this.min );',
	'}',
	'else if (this.shapeIn === "linear")',
	'{',
	'range = this.plateauMin - this.min;',
	'crisp.push( this.min + range * veracity );',
	'}',
	'else if (this.shapeIn === "sigmoid")',
	'{',
	'mid = (this.plateauMin + this.min) / 2;',
	'crisp.push( inverseLogistic(veracity, mid, 0, 1, 1) );',
	'}',
	'else if (this.shapeIn === "gaussian")',
	'{',
	'var width = this.plateauMin - this.min;',
	'var g = inverseGaussian( veracity, 0, 1, this.plateauMin, width);',
	'crisp.push( g[0] );',
	'}',
	'if (this.shapeOut === "constant" && veracity == 1)',
	'{',
	'crisp.push(this.max);',
	'}',
	'if (this.shapeOut === "square")',
	'{',
	'if (veracity >= 0.5) crisp.push( this.plateauMax );',
	'else crisp.push( this.max );',
	'}',
	'else if (this.shapeOut === "linear")',
	'{',
	'range = this.max - this.plateauMax;',
	'crisp.push( this.max + 1 - (range * veracity) );',
	'}',
	'else if (this.shapeOut === "sigmoid")',
	'{',
	'mid = (this.plateauMax + this.max) / 2;',
	'crisp.push( inverseLogistic( 1-veracity, mid, 0, 1, 1 ) );',
	'}',
	'else if (this.shapeOut === "gaussian")',
	'{',
	'width = this.max - this.plateauMax;',
	'var g = inverseGaussian( 1-veracity, 0, 1, this.plateauMax, width);',
	'crisp.push( g[1] );',
	'}',
	'for(var i = 0, num = crisp.length; i < num; i++)',
	'{',
	'if ( crisp[i] > this.max ) crisp[i] = this.max;',
	'if ( crisp[i] < this.min ) crisp[i] = this.min;',
	'}',
	'return crisp.sort();',
	'},',
	'crispify: function ( quantifier, veracity )',
	'{',
	'quantifier = getQuantifier(quantifier);',
	'var v;',
	'if (typeof veracity === "undefined") v = quantifier();',
	'else if (veracity instanceof FuzzyVeracity) v = veracity.veracity;',
	'else v = veracity;',
	'v = quantifier(v, true).veracity;',
	'return this.getValues( v );',
	'}',
	'};',
	'function FuzzyValue( val )',
	'{',
	'if (typeof unit === "undefined") unit = "";',
	'if (typeof val === "undefined") val = 0;',
	'this.val = val;',
	'this.sets = [];',
	'this.report = [];',
	'this.reportEnabled = false;',
	'this.numRules = 0;',
	'}',
	'FuzzyValue.prototype = {',
	'IS: function(fuzzyset, quantifier)',
	'{',
	'var v = fuzzyset.contains( this, quantifier );',
	'return v;',
	'},',
	'IS_NOT: function (fuzzyset, quantifier)',
	'{',
	'var x = fuzzyset.contains( this.val, quantifier );',
	'return x.NEGATE();',
	'},',
	'SET: function ( fuzzyset,  quantifier, v )',
	'{',
	'if (typeof v === "undefined") v = new FuzzyVeracity(1);',
	'quantifier = getQuantifier(quantifier);',
	'this.numRules++;',
	'v.ruleNum = this.numRules;',
	'for (var i = 0, num = this.sets.length; i < num; i++)',
	'{',
	'var s = this.sets[i].fuzzyset;',
	'if (fuzzyset.name == s.name)',
	'{',
	'this.sets[i].quantifiers.push(quantifier);',
	'this.sets[i].veracities.push(v);',
	'return;',
	'}',
	'}',
	'var s = {};',
	's.fuzzyset = fuzzyset;',
	's.quantifiers = [quantifier];',
	's.veracities = [v];',
	'this.sets.push( s );',
	'},',
	'crispify: function ( clearSets )',
	'{',
	'if (typeof clearSets === "undefined") clearSets = true;',
	'if (this.sets.length == 0) return this.val;',
	'var crisp = 0;',
	'this.report = [];',
	'function ruleSorter(a, b)',
	'{',
	'return a.number - b.number;',
	'}',
	'var sumWeights = 0;',
	'for (var i = 0, num = this.sets.length; i < num; i++)',
	'{',
	'var s = this.sets[i];',
	'for( var j = 0, numV = s.veracities.length; j < numV; j++)',
	'{',
	'var v = s.veracities[j];',
	'var q = s.quantifiers[j];',
	'var vals = s.fuzzyset.crispify( q, v );',
	'var val;',
	'var ver;',
	'val = mean(vals);',
	'crisp += val * v.veracity;',
	'ver = v.veracity;',
	'sumWeights += ver;',
	'if (this.reportEnabled)',
	'{',
	'for (var iVals = 0, numVals = vals.length; iVals < numVals; iVals++)',
	'{',
	'vals[iVals] = Math.round(vals[iVals]*1000)/1000;',
	'}',
	'var reportRule = [];',
	'reportRule.push( "Rule #" + v.ruleNum +": Set " + fuzzyset.toString() + " (" + q.toString() + ")" );',
	'reportRule.push( "Gives val: " + Math.round(val*1000)/1000 + " from these values: [ " + vals.join(", ") + " ]");',
	'reportRule.push( "With a veracity of: " + Math.round(ver*1000)/1000 );',
	'reportRule.number = v.ruleNum;',
	'this.report.push( reportRule );',
	'}',
	'}',
	'}',
	'if (sumWeights != 0) crisp = crisp / sumWeights;',
	'if (this.reportEnabled) this.report.sort(ruleSorter);',
	'if (clearSets)',
	'{',
	'this.val = crisp;',
	'this.sets = [];',
	'}',
	'return crisp;',
	'},',
	'toNumber: this.crispify,',
	'toFloat: this.crispify,',
	'defuzzify: this.crispify',
	'};',
	'function FuzzyVeracity( veracity )',
	'{',
	'if (typeof above === "undefined") above = false;',
	'this.veracity = veracity;',
	'}',
	'FuzzyVeracity.prototype = {',
	'NEGATE: function()',
	'{',
	'return new FuzzyVeracity( 1 - this.veracity );',
	'},',
	'AND: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = Math.min(x, y);',
	'return new FuzzyVeracity( v );',
	'},',
	'OR: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = Math.max(x, y);',
	'return new FuzzyVeracity( v );',
	'},',
	'XOR: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = x+y - 2*Math.min(x,y);',
	'return new FuzzyVeracity( v );',
	'},',
	'IS_NOT: this.XOR,',
	'DIFFERENT: this.XOR,',
	'NXR: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = 1-x-y + 2*Math.min(x,y);',
	'return new FuzzyVeracity( v );',
	'},',
	'IS: this.NXR,',
	'EQUALS: this.NXR,',
	'IMPLIES: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = 1-Math.min(x, 1-y);',
	'return new FuzzyVeracity( v );',
	'},',
	'WITH: this.IMPLIES,',
	'HAS: this.IMPLIES,',
	'DOES_NOT_IMPLY: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = Math.min(x, 1-y);',
	'return new FuzzyVeracity( v );',
	'},',
	'WITHOUT: this.DOES_NOT_IMPLY,',
	'DOES_NOT_HAVE: this.DOES_NOT_IMPLY,',
	'NAND: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = 1 - Math.min(x, y);',
	'return new FuzzyVeracity( v );',
	'},',
	'NOT_BOTH: this.NAND,',
	'NOR: function( other )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = 0;',
	'v = 1 - Math.max(x, y);',
	'return new FuzzyVeracity( v );',
	'},',
	'NONE: this.NOR,',
	'WEIGHTED: function( other, weight )',
	'{',
	'var x = this.veracity;',
	'var y = other.veracity;',
	'var v = (1-w)*x +  w*y;',
	'return new FuzzyVeracity( v );',
	'}',
	'};',
	'function FuzzyLogic( )',
	'{',
	'this.veracity = new FuzzyVeracity(0);',
	'this.sets = [];',
	'}',
	'FuzzyLogic.prototype = {',
	'newValue: function (val, unit)',
	'{',
	'return new FuzzyValue( val, unit );',
	'},',
	'newVeracity: function (veracity)',
	'{',
	'return new FuzzyVeracity(veracity);',
	'},',
	'newSet: function ( name, extremeValue, referenceValue, shape, shapeAbove, plateauMin, plateauMax)',
	'{',
	'return new FuzzySet(name, extremeValue, referenceValue, shape, shapeAbove, plateauMin, plateauMax);',
	'},',
	'IF: function ( veracity )',
	'{',
	'this.veracity = veracity;',
	'return veracity;',
	'},',
	'THEN: function ( val, fuzzyset, quantifier )',
	'{',
	'val.SET(fuzzyset, quantifier, this.veracity);',
	'}',
	'};',
	'function getQuantifier( name )',
	'{',
	'if (typeof name === "undefined") name = "moderately";',
	'if (name == "not" || name == "less") {',
	'function qObj (v, inverse) {',
	'if (typeof v === "undefined") return 0;',
	'var p = inverse ? 0 : 1;',
	'return new FuzzyVeracity( p );',
	'}',
	'return qObj;',
	'}',
	'if (name == "slightly") return createQuantifier( 1/3 );',
	'if (name == "somewhat") return createQuantifier( 0.5 );',
	'if (name == "moderately") return createQuantifier( 1 );',
	'if (name == "very") return createQuantifier( 2 );',
	'if (name == "extremely") return createQuantifier( 3 );',
	'function qObj (v, inverse) {',
	'if (typeof v === "undefined") return 1;',
	'var p = inverse ? 1 : 0;',
	'return new FuzzyVeracity( p );',
	'}',
	'return qObj;',
	'}',
	'function createQuantifier( q )',
	'{',
	'function qObj (v, inverse) {',
	'if (typeof v === "undefined") return Math.pow( 0.5, 1/q);',
	'var p = inverse ? 1/q : q;',
	'return new FuzzyVeracity( Math.pow(v, p) );',
	'}',
	'return qObj;',
	'}'
	].join('\n');
DuAEExpression.Library["FuzzyLogic"].requirements = ["gaussian","inverseGaussian","inverseLogistic","logistic","mean"];

/**
 * A (very) simplified version of FuzzyLogics, better for performance with expressions.<br />
 * See {@link https://github.com/Nico-Duduf/DuFuzzyLogic} for more explanations
 * @class
 * @classdesc A (very) simplified version of FuzzyLogics, better for performance with expressions.
 * See {@link https://github.com/Nico-Duduf/DuFuzzyLogic} for more explanations
 * @author Nicolas "Duduf" Dufresne
 * @license GPL-v3
 * @copyright 2020-2022 Nicolas Dufresne and contributors
 * @param {Number} v The original veracity, must be in the range [0.0, 1.0]
 * @param {Number} [f=1] A factor to adjust the <i>importance</i> of the veracity, when compared to others.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["FuzzyVeracity"] = {};
DuAEExpression.Library["FuzzyVeracity"].expression = ['function FuzzyVeracity( v, f )',
	'{',
	'if (typeof f === \'undefined\') f = 1;',
	'this.veracity = v;',
	'this.factor = f;',
	'}',
	'FuzzyVeracity.prototype = {',
	'NEGATE: function() {',
	'return new FuzzyVeracity(1 - this.veracity, 0.5);',
	'},',
	'AND: function(other) {',
	'var x = this.veracity * this.factor;',
	'var y = other.veracity * other.factor;',
	'var v = x*y;',
	'return new FuzzyVeracity( v );',
	'},',
	'OR: function(other) {',
	'var x = this.veracity * this.factor;',
	'var y = other.veracity * other.factor;',
	'var v = x + y - x*y;',
	'return new FuzzyVeracity( v );',
	'},',
	'DIFFERENT: function(other) {',
	'var x = this.veracity * this.factor;',
	'var y = other.veracity * other.factor;',
	'var v = x+y - 2*x*y;',
	'return new FuzzyVeracity( v );',
	'},',
	'EQUALS: function(other) {',
	'var x = this.veracity * this.factor;',
	'var y = other.veracity * other.factor;',
	'var v = 1-x-y + 2*x*y;',
	'return new FuzzyVeracity( v );',
	'},',
	'};'
	].join('\n');
DuAEExpression.Library["FuzzyVeracity"].requirements = [];

/**
 * Animates the property using the given keyframes
 * @function
 * @param {Object[]} keyframes The keyframes. An object with four properties:<br/>
 * <code>value</code> The value of the keyframe<br />
 * <code>time</code> The time of the keyframe<br />
 * <code>interpolation</code> (optional. Default: linear) A function taking 5 arguments to interpolate between this keyframe and the next one<br />
 * <code>params</code> (optional.) A sixth argument passed to the interpolation function. To be used with DuAEF interpolations.<br />
 * Note that the keyframes <strong>must be sorted</strong>. The function does not sort them, as it would have a bad impact on performance.
 * @param {string} [loopOut='none'] One of 'none', 'cycle', 'pingpong'.
 * @param {string} [loopIn='none'] One of 'none', 'cycle', 'pingpong'.
 * @param {float} [time=time] Use this to control how time flows.
 * @example
 * var keyframes = [
 *    {value: 0, time: 1, interpolation: linear},
 *    {value: 180, time: 2, interpolation: gaussianInterpolation, params: -0.5}, //You need to include the gaussianInterpolation function from DuAEF
 *    {value: 250, time: 4, interpolation: ease},
 *    {value: 360, time: 5},
 * ];
 * animate(keyframes, 'cycle', 'pingpong');
 * @return {number} the animated value.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["animate"] = {};
DuAEExpression.Library["animate"].expression = ['function animate(ks, loopOut, loopIn, ct) {',
	'if (ks.length == 0) return value;',
	'if (ks.length == 1) return ks[0].value;',
	'if (typeof loopOut === \'undefined\') loopOut = \'none\';',
	'if (typeof loopIn === \'undefined\') loopIn = \'none\';',
	'if (typeof ct === \'undefined\') ct = time;',
	'var startTime = ks[0].time;',
	'var endTime = ks[ks.length-1].time;',
	'var duration = endTime - startTime;',
	'if ( ct >= endTime )',
	'{',
	'if ( loopOut == \'cycle\' ) ct = ((ct - startTime) % duration) + startTime;',
	'else if ( loopOut == \'pingpong\' ) {',
	'var d = duration * 2;',
	'ct = (ct-startTime) % d;',
	'if (ct > duration) ct = d - ct;',
	'ct += startTime;',
	'}',
	'}',
	'else if ( ct < startTime) {',
	'if ( loopIn == \'cycle\' ) ct = ((ct - startTime) % duration) + startTime + duration;',
	'else if ( loopIn == \'pingpong\' ) {',
	'var d = duration * 2;',
	'ct += d;',
	'ct = (ct-startTime) % d;',
	'if (ct > duration) ct = d - ct;',
	'ct += startTime;',
	'}',
	'}',
	'for (var i = 0; i < ks.length; i++) {',
	'var k = ks[i];',
	'if (k.time > ct && i == 0) return k.value;',
	'if (k.time == ct) return k.value;',
	'if (k.time < ct) {',
	'if (i == ks.length - 1) return k.value;',
	'var nk = ks[i+1];',
	'if (nk.time < ct) continue;',
	'if (typeof k.interpolation === \'undefined\') return linear(ct, k.time, nk.time, k.value, nk.value);',
	'if (typeof k.params === \'undefined\') return k.interpolation(ct, k.time, nk.time, k.value, nk.value);',
	'else return k.interpolation(ct, k.time, nk.time, k.value, nk.value, k.params);',
	'}',
	'}',
	'return value;',
	'}'
	].join('\n');
DuAEExpression.Library["animate"].requirements = [];

/**
 * Interpolates a value with a bezier curve.<br />
 * This method can replace <code>linear()</code> and <code>ease()</code> with a custom bézier interpolation.
 * @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[]} [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.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["bezierInterpolation"] = {};
DuAEExpression.Library["bezierInterpolation"].expression = ['function bezierInterpolation(t, tMin, tMax, value1, value2, bezierPoints) {',
	'if (typeof tMin === \'undefined\') tMin = 0;',
	'if (typeof tMax === \'undefined\') tMax = 1;',
	'if (typeof value1 === \'undefined\') value1 = 0;',
	'if (typeof value2 === \'undefined\') value2 = 0;',
	'if (typeof bezierPoints === \'undefined\') 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 = 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));',
	'}',
	'}'
	].join('\n');
DuAEExpression.Library["bezierInterpolation"].requirements = [];

/**
 * Interpolates a value with an exponential function.<br />
 * This method can replace <code>linear()</code> and <code>ease()</code> with a gaussian interpolation.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @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.
 * @requires linearExtrapolation
 * @category ExpressionLibrary
 */
DuAEExpression.Library["expInterpolation"] = {};
DuAEExpression.Library["expInterpolation"].expression = ['function expInterpolation(t, tMin, tMax, vMin, vMax, rate)',
	'{',
	'if (typeof tMin === \'undefined\') tMin = 0;',
	'if (typeof tMax === \'undefined\') tMax = 1;',
	'if (typeof value1 === \'undefined\') value1 = 0;',
	'if (typeof value2 === \'undefined\') value2 = 0;',
	'if (typeof rate === \'undefined\') rate = 1;',
	'if (rate == 0) return linearExtrapolation(t, tMin, tMax, vMin, vMax);',
	'tMax = ( tMax - tMin ) * rate;',
	't = ( t - tMin ) * rate;',
	'var m = Math.exp(tMax);',
	't = Math.exp(t);',
	'return linearExtrapolation(t, 1, m, vMin, vMax);',
	'}'
	].join('\n');
DuAEExpression.Library["expInterpolation"].requirements = ["linearExtrapolation"];

/**
 * Interpolates a value with a gaussian function.<br />
 * This method can replace <code>linear()</code> and <code>ease()</code> with a gaussian interpolation.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @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.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["gaussianInterpolation"] = {};
DuAEExpression.Library["gaussianInterpolation"].expression = ['function gaussianInterpolation( t, tMin, tMax, value1, value2, rate )',
	'{',
	'if (typeof tMin === \'undefined\') tMin = 0;',
	'if (typeof tMax === \'undefined\') tMax = 1;',
	'if (typeof value1 === \'undefined\') value1 = 0;',
	'if (typeof value2 === \'undefined\') value2 = 0;',
	'if (typeof rate === \'undefined\') rate = 0;',
	'if (t != tMin)',
	'{',
	'var newValue1 = gaussianInterpolation( tMin, tMin, tMax, value1, value2, rate );',
	'var offset = newValue1 - value1;',
	'value1 = value1 - offset;',
	'}',
	'if (rate < 0) rate = rate*10;',
	'rate = linear(t, tMin, tMax, 0.25, rate);',
	'var r = ( 1 - rate );',
	'var fwhm = (tMax-tMin) * r;',
	'var center = tMax;',
	'if (t >= tMax) return value2;',
	'if (fwhm === 0 && t == center) return value2;',
	'else if (fwhm === 0) 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;',
	'return result;',
	'}'
	].join('\n');
DuAEExpression.Library["gaussianInterpolation"].requirements = [];

/**
 * Converts a Gaussian rate (as used with <code>gaussianInterpolation</code>) to the closest possible Bézier controls for use with <code>bezierInterpolation</code>.
 * @function
 * @param {number} rate The raising speed in the range [-1.0, 1.0].
 * @return {number} the value.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["gaussianRateToBezierPoints"] = {};
DuAEExpression.Library["gaussianRateToBezierPoints"].expression = ['function gaussianRateToBezierPoints(rate) {',
	'var i = 0;',
	'var o = 1;',
	'if (rate <= -.025) {',
	'i = linear(rate, -0.4, -0.025, 0.17, 0.415);',
	'o = 1-easeIn(rate, -0.4, -0.025, 1, 0.415);',
	'}',
	'else {',
	'i = linear(rate, -0.025, 0.7, 0.415, 1);',
	'o = 1-easeOut(rate, -0.025, 0.7, 0.415, 0.15);',
	'}',
	'return [i,0,o,1];',
	'}'
	].join('\n');
DuAEExpression.Library["gaussianRateToBezierPoints"].requirements = [];

/**
 * Integrates the (linear) keyframe values. Useful to animate frequencies!
 * cf. {@link http://www.motionscript.com/articles/speed-control.html} for more explanation.
 * @function
 * @param {Property} [prop=thisProperty] The property with the keyframes.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["integrateLinearKeys"] = {};
DuAEExpression.Library["integrateLinearKeys"].expression = ['function integrateLinearKeys( prop ) {',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'var nK = prop.numKeys;',
	'if (nK < 2) return prop.value*(time - inPoint);',
	'if (prop.key(1).time > time ) return prop.value*(time - inPoint);',
	'var result = prop.key(1).value * (prop.key(1).time - inPoint);',
	'for (var i = 2; i <= nK; i++){',
	'if ( prop.key(i).time > time ) break;',
	'var k1 = prop.key(i-1);',
	'var k2 = prop.key(i);',
	'result += (k1.value + k2.value) * (k2.time - k1.time)/2;',
	'}',
	'result += (prop.value + prop.key(i-1).value) * (time - prop.key(i-1).time) / 2;',
	'return result;',
	'}'
	].join('\n');
DuAEExpression.Library["integrateLinearKeys"].requirements = [];

/**
 * Clamps a value, but with a smooth interpolation according to a softness parameter
 * @function
 * @param {number|number[]} value The value to limit
 * @param {number|number[]|null} [min] The minimum value
 * @param {number|number[]|null} [max] The maximum value
 * @param {number} [softness=0] The softness, a value corresponding value, from which the interpolation begins to slow down
 * @category ExpressionLibrary
 */
DuAEExpression.Library["limit"] = {};
DuAEExpression.Library["limit"].expression = ['function limit(val, min, max, softness) {',
	'if (typeof min === \'undefined\') min = null;',
	'if (typeof max === \'undefined\') max = null;',
	'if (typeof softness === \'undefined\') softness = 0;',
	'if (min == null && max == null) return val;',
	'if (typeof val.length !== \'undefined\') {',
	'var n = 0;',
	'if (min !== null) {',
	'if (typeof min.length === \'undefined\') {',
	'min = [min];',
	'while(min.length < val.length) min.push(min[0]);',
	'}',
	'n = Math.max(val.length, min.length);',
	'}',
	'else if (max !== null) {',
	'if (typeof max.length === \'undefined\') {',
	'max = [max];',
	'while(max.length < val.length) max.push(max[0]);',
	'}',
	'n = Math.max(val.length, max.length);',
	'}',
	'for (var i = 0; i < n; i++) {',
	'if (min !== null && max !== null) val[i] = limit(val[i], min[i], max[i], softness);',
	'else if (min !== null) val[i] = limit(val[i], min[i], null, softness);',
	'else if (max !== null) val[i] = limit(val[i], null, max[i], softness);',
	'}',
	'return val;',
	'}',
	'if (max != null) {',
	'if (typeof max.length !== \'undefined\') max = max[0];',
	'max = max - softness;',
	'if ( val > max ) {',
	'if (softness == 0) return max;',
	'return max + softness - softness / ( 1 + (val - max)/softness);',
	'}',
	'}',
	'if (min != null) {',
	'if (typeof min.length !== \'undefined\') min = min[0];',
	'min = min + softness;',
	'if (val < min && min != null) {',
	'if (softness == 0) return min;',
	'return min - softness + softness / (1 + (min - val)/softness);',
	'}',
	'}',
	'return val;',
	'}'
	].join('\n');
DuAEExpression.Library["limit"].requirements = [];

/**
 * Interpolates a value with a linear function, but also extrapolates it outside of known values.<br />
 * This method can replace <code>linear()</code>, adding extrapolation.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {number} t The value to interpolate and extrapolate
 * @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
 * @return {number} the value.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["linearExtrapolation"] = {};
DuAEExpression.Library["linearExtrapolation"].expression = ['function linearExtrapolation( t, tMin, tMax, value1, value2 )',
	'{',
	'if (tMax == tMin) return (value1+value2) / 2;',
	'return value1 + (( t - tMin) / (tMax - tMin)) * (value2 - value1);',
	'}'
	].join('\n');
DuAEExpression.Library["linearExtrapolation"].requirements = [];

/**
 * Interpolates a value with a logarithmic function.<br />
 * This method can replace <code>linear()</code> and <code>ease()</code> with a gaussian interpolation.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @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.
 * @requires linearExtrapolation
 * @category ExpressionLibrary
 */
DuAEExpression.Library["logInterpolation"] = {};
DuAEExpression.Library["logInterpolation"].expression = ['function logInterpolation(t, tMin, tMax, vMin, vMax, rate)',
	'{',
	'if (typeof tMin === \'undefined\') tMin = 0;',
	'if (typeof tMax === \'undefined\') tMax = 1;',
	'if (typeof value1 === \'undefined\') value1 = 0;',
	'if (typeof value2 === \'undefined\') value2 = 0;',
	'if (typeof rate === \'undefined\') rate = 1;',
	'if (rate == 0) return linearExtrapolation(t, tMin, tMax, vMin, vMax);',
	'tMax = ( tMax - tMin ) * rate + 1;',
	't = ( t - tMin ) * rate + 1;',
	'if (t <= 1) return vMin;',
	'var m = Math.log(tMax);',
	'var v = Math.log(t);',
	'return linearExtrapolation(v, 0, m, vMin, vMax);',
	'}'
	].join('\n');
DuAEExpression.Library["logInterpolation"].requirements = ["linearExtrapolation"];

/**
 * Interpolates a value with a logistic (sigmoid) function.<br />
 * This method can replace <code>linear()</code> and <code>ease()</code> with a gaussian interpolation.
 * @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.s
 * @requires logistic
 * @requires linearExtrapolation
 * @category ExpressionLibrary
 */
DuAEExpression.Library["logisticInterpolation"] = {};
DuAEExpression.Library["logisticInterpolation"].expression = ['function logisticInterpolation( t, tMin, tMax, value1, value2, rate, tMid )',
	'{',
	'if (typeof tMin === \'undefined\') tMin = 0;',
	'if (typeof tMax === \'undefined\') tMax = 1;',
	'if (typeof value1 === \'undefined\') value1 = 0;',
	'if (typeof value2 === \'undefined\') value2 = 0;',
	'if (typeof rate === \'undefined\') rate = 1;',
	'if (typeof tMid === \'undefined\') tMid = (tMin+tMax)/2;',
	'if (rate == 0) return linearExtrapolation(t, tMin, tMax, value1, value2);',
	't = logistic( t, tMid, tMin, tMax, rate);',
	'var m = logistic( tMin, tMid, tMin, tMax, rate);',
	'var M = logistic( tMax, tMid, tMin, tMax, rate);',
	'return linearExtrapolation( t, m, M, value1, value2);',
	'}'
	].join('\n');
DuAEExpression.Library["logisticInterpolation"].requirements = ["logistic","linearExtrapolation"];

/**
 * Gets the key immediately before the given time
 * @function
 * @param {number} [t=time] Time at which to get the key
 * @param {Property} [prop=thisProperty] The property from which to get the key
 * @return {Key|null} The key, or null if there's no key before.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getNextKey"] = {};
DuAEExpression.Library["getNextKey"].expression = ['function getNextKey(t, prop) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'if (prop.numKeys == 0) return null;',
	'var nKey = prop.nearestKey(t);',
	'if (nKey.time >= t) return nKey;',
	'if (nKey.index < prop.numKeys) return prop.key(nKey.index + 1);',
	'return null;',
	'}'
	].join('\n');
DuAEExpression.Library["getNextKey"].requirements = [];

/**
 * Gets the next key where there is no motion after it
 * @function
 * @param {number} [t=time] Time at which to get the key
 * @param {Property} [prop=thisProperty] The property from which to get the key
 * @return {Key|null} The key, or null if there's no key before.
 * @requires isStill
 * @requires getNextKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getNextStopKey"] = {};
DuAEExpression.Library["getNextStopKey"].expression = ['function getNextStopKey(t, prop) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'var k = getNextKey(t, prop);',
	'if (!k) return null;',
	'var i = k.index;',
	'while (i < prop.numKeys) {',
	'if (isStill( k.time + thisComp.frameDuration )) return k;',
	'k = key(k.index + 1);',
	'}',
	'return k;',
	'}'
	].join('\n');
DuAEExpression.Library["getNextStopKey"].requirements = ["isStill","getNextKey"];

/**
 * Gets the key immediately before the given time
 * @function
 * @param {number} [t=time] Time at which to get the key
 * @param {Property} [prop=thisProperty] The property from which to get the key
 * @return {Key|null} The key, or null if there's no key before.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPrevKey"] = {};
DuAEExpression.Library["getPrevKey"].expression = ['function getPrevKey(t, prop) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'if (prop.numKeys == 0) return null;',
	'var nKey = prop.nearestKey(t);',
	'if (nKey.time <= t) return nKey;',
	'if (nKey.index > 1) return prop.key(nKey.index - 1);',
	'return null;',
	'}'
	].join('\n');
DuAEExpression.Library["getPrevKey"].requirements = [];

/**
 * Gets the previous key where there is no motion before it
 * @function
 * @param {number} [t=time] Time at which to get the key
 * @param {Property} [prop=thisProperty] The property from which to get the key
 * @return {Key|null} The key, or null if there's no key before.
 * @requires isStill
 * @requires getPrevKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPrevStartKey"] = {};
DuAEExpression.Library["getPrevStartKey"].expression = ['function getPrevStartKey(t, prop) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'var k = getPrevKey(t, prop);',
	'if (!k) return null;',
	'var i = k.index;',
	'while (i > 0) {',
	'if (isStill( k.time - thisComp.frameDuration )) return k;',
	'k = key(k.index - 1);',
	'}',
	'return k;',
	'}'
	].join('\n');
DuAEExpression.Library["getPrevStartKey"].requirements = ["isStill","getPrevKey"];

/**
 * Checks if current time is after the time of the last key in the property
 * @function
 * @return {boolean} true if time is > lastkey.time
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isAfterLastKey"] = {};
DuAEExpression.Library["isAfterLastKey"].expression = ['function isAfterLastKey() {',
	'if (numKeys == 0) return false;',
	'var nKey = nearestKey(time);',
	'return nKey.time <= time && nKey.index == numKeys;',
	'}'
	].join('\n');
DuAEExpression.Library["isAfterLastKey"].requirements = [];

/**
 * Checks if the key is a maximum or minimum
 * @function
 * @param {Keyframe} k The key to check
 * @param {int} axis The axis to check for multi-dimensionnal properties
 * @return {boolean} true if the key is a maximum or minimum
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isKeyTop"] = {};
DuAEExpression.Library["isKeyTop"].expression = ['function isKeyTop(k, axis) {',
	'var prevSpeed = velocityAtTime(k.time - thisComp.frameDuration/2);',
	'var nextSpeed = velocityAtTime(k.time + thisComp.frameDuration/2);',
	'if (value instanceof Array) {',
	'prevSpeed = prevSpeed[axis];',
	'nextSpeed = nextSpeed[axis];',
	'}',
	'if (Math.abs(prevSpeed) < 0.01 || Math.abs(nextSpeed) < 0.01) return true;',
	'return prevSpeed * nextSpeed < 0;',
	'}'
	].join('\n');
DuAEExpression.Library["isKeyTop"].requirements = [];

/**
 * Bounce, to be used when the speed is 0.
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all subsequent frames.
 * @param {float} elasticity The elasticity, which controls the amplitude and frequence according to the last known velocity
 * @param {float} damping Damping
 * @param {function} [vAtTime=valueAtTime] A function to replace valueAtTime. Use this to loop after an expression+keyframe controlled animation, by providing a function used to generate the animation.
 * @returns {float|float[]} The new value
 * @function
 * @requires getPrevKey
 * @requires bezierInterpolation
 * @category ExpressionLibrary
 */
DuAEExpression.Library["bounce"] = {};
DuAEExpression.Library["bounce"].expression = ['function bounce(t, elasticity, damping, vAtTime) {',
	'if (elasticity == 0) return value;',
	'if (numKeys < 2) return value;',
	'if (nearestKey(t).index == 1) return value;',
	'var pVelocity = ( vAtTime(t) - vAtTime( t - .01 ) ) * 100;',
	'var pSpeed = length( pVelocity );',
	'if (pSpeed >= 0.001 ) return value;',
	'var bounceStart = 0;',
	'var bounceTime = 0;',
	'var bouceKey = getPrevKey(t, thisProperty);',
	'bounceStart = bouceKey.time;',
	'bounceTime = t - bounceStart;',
	'pVelocity = ( vAtTime( bounceStart ) - vAtTime( bounceStart - thisComp.frameDuration * .5 ) );',
	'var bounceValue = ( pVelocity / thisComp.frameDuration ) ;',
	'var cycleDamp = Math.exp(bounceTime * damping * .1);',
	'var damp = Math.exp(bounceTime * damping) / (elasticity / 2);',
	'var cycleDuration = 1 / (elasticity * 2);',
	'cycleDuration = Math.round(timeToFrames(cycleDuration));',
	'cycleDuration = framesToTime(cycleDuration);',
	'var midDuration = cycleDuration / 2;',
	'var maxValue = bounceValue * midDuration;',
	'var cycvarime = bounceTime;',
	'var numEndCycles = 1;',
	'while (cycvarime > cycleDuration) {',
	'cycvarime = cycvarime - cycleDuration;',
	'cycleDuration = cycleDuration / cycleDamp;',
	'cycleDuration = Math.round(timeToFrames(cycleDuration));',
	'if (cycleDuration < 2) {',
	'cycleDuration = 2;',
	'numEndCycles++;',
	'}',
	'cycleDuration = framesToTime(cycleDuration);',
	'midDuration = cycleDuration / 2;',
	'maxValue = bounceValue * midDuration / damp;',
	'if (numEndCycles > 100 / damping && maxValue < 0.001 ) return value;',
	'}',
	'if (cycvarime < midDuration) bounceValue = bezierInterpolation(cycvarime, 0, midDuration, 0, maxValue, [0, .1, .33, 1]);',
	'else bounceValue = bezierInterpolation(cycvarime, midDuration, cycleDuration, maxValue, 0, [.66, 0, 1, .9]);',
	'var prevValue = valueAtTime(bounceStart - thisComp.frameDuration);',
	'var startValue = valueAtTime(bounceStart);',
	'if (value instanceof Array) {',
	'for (var i = 0; i < prevValue.length; i++) {',
	'if (prevValue[i] > startValue[i]) bounceValue[i] = Math.abs(fThrough[i]);',
	'if (prevValue[i] < startValue[i]) bounceValue[i] = -Math.abs(fThrough[i]);',
	'}',
	'} else {',
	'if (prevValue > startValue) bounceValue = Math.abs(bounceValue);',
	'if (prevValue < startValue) bounceValue = -Math.abs(bounceValue);',
	'}',
	'return bounceValue + value;',
	'}'
	].join('\n');
DuAEExpression.Library["bounce"].requirements = ["getPrevKey","bezierInterpolation"];

/**
 * Animatable equivalent to loopIn('continue').
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all previous frames.
 * @param {float} [damping=0] Exponentially attenuates the effect over time
 * @returns {float|float[]} The new value
 * @function
 * @requires getNextKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["continueIn"] = {};
DuAEExpression.Library["continueIn"].expression = ['function continueIn(t, damping) {',
	'if (numKeys <= 1) return value;',
	'var firstKey = getNextKey(t, thisProperty);',
	'if (!firstKey) return value;',
	'var firstVelocity = velocityAtTime(firstKey.time + 0.001);',
	'var timeSpent = firstKey.time - t;',
	'var damp = Math.exp(timeSpent * damping);',
	'return (-timeSpent * firstVelocity) / damp + firstKey.value;',
	'}'
	].join('\n');
DuAEExpression.Library["continueIn"].requirements = ["getNextKey"];

/**
 * Animatable equivalent to loopOut('continue').
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all subsequent frames.
 * @param {float} [damping=0] Exponentially attenuates the effect over time
 * @returns {float|float[]} The new value
 * @function
 * @requires getNextKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["continueOut"] = {};
DuAEExpression.Library["continueOut"].expression = ['function continueOut(t, damping) {',
	'if (numKeys <= 1) return value;',
	'var lastKey = getPrevKey(t, thisProperty);',
	'if (!lastKey) return value;',
	'var lastVelocity = velocityAtTime(lastKey.time - 0.001);',
	'var timeSpent = t - lastKey.time;',
	'var damp = Math.exp(timeSpent * damping);',
	'return lastKey.value + (timeSpent * lastVelocity) / damp;',
	'}'
	].join('\n');
DuAEExpression.Library["continueOut"].requirements = ["getNextKey"];

/**
 * Animatable equivalent to loopIn('cycle') and loopIn('offset').
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all previous frames.
 * @param {int} nK The number of keyframes to loop. Use 0 to loop all keyframes
 * @param {Boolean} o Wether to offset or cycle
 * @param {function} [vAtTime=valueAtTime] A function to replace valueAtTime. Use this to loop after an expression+keyframe controlled animation, by providing a function used to generate the animation.
 * @param {float} [damping=0] Exponentially attenuates the effect over time
 * @returns {float|float[]} The new value
 * @function
 * @requires getNextKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["cycleIn"] = {};
DuAEExpression.Library["cycleIn"].expression = ['function cycleIn(t, nK, o, vAtTime, damping) {',
	'var currentValue = vAtTime(t);',
	'if (numKeys <= 1) return currentValue;',
	'var lastKeyIndex = numKeys;',
	'var firstKey = getNextKey(t, thisProperty);',
	'if (!firstKey) return currentValue;',
	'if (firstKey.index == lastKeyIndex) return currentValue;',
	'if (nK >= 2) {',
	'nK = nK - 1;',
	'lastKeyIndex = firstKey.index + nK;',
	'if (lastKeyIndex > numKeys) lastKeyIndex = numKeys;',
	'}',
	'var loopStartTime = firstKey.time;',
	'var loopEndTime = key(lastKeyIndex).time;',
	'var loopDuration = loopEndTime - loopStartTime;',
	'if (t >= loopStartTime) return currentValue;',
	'var timeSpent = loopStartTime - t;',
	'var numLoops = Math.floor(timeSpent / loopDuration);',
	'var loopTime = loopDuration - timeSpent;',
	'if (numLoops > 0) loopTime = loopDuration - (timeSpent - numLoops * loopDuration);',
	'var r = vAtTime(loopStartTime + loopTime);',
	'if (o) r -= (key(lastKeyIndex).value - firstKey.value) * (numLoops + 1);',
	'var damp = Math.exp(timeSpent * damping);',
	'return (r-currentValue) / damp + currentValue;',
	'}'
	].join('\n');
DuAEExpression.Library["cycleIn"].requirements = ["getNextKey"];

/**
 * Animatable equivalent to loopOut('cycle') and loopOut('offset').
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all subsequent frames.
 * @param {int} nK The number of keyframes to loop. Use 0 to loop all keyframes
 * @param {Boolean} o Wether to offset or cycle
 * @param {function} [vAtTime=valueAtTime] A function to replace valueAtTime. Use this to loop after an expression+keyframe controlled animation, by providing a function used to generate the animation.
 * @param {float} [damping=0] Exponentially attenuates the effect over time
 * @returns {float|float[]} The new value
 * @function
 * @requires getPrevKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["cycleOut"] = {};
DuAEExpression.Library["cycleOut"].expression = ['function cycleOut(t, nK, o, vAtTime, damping) {',
	'var currentValue = vAtTime(t);',
	'if (numKeys <= 1) return currentValue;',
	'var firstKeyIndex = 1;',
	'var lastKey = getPrevKey(t, thisProperty);',
	'if (!lastKey) return currentValue;',
	'if (lastKey.index == firstKeyIndex) return currentValue;',
	'if (nK >= 2) {',
	'nK = nK - 1;',
	'firstKeyIndex = lastKey.index - nK;',
	'if (firstKeyIndex < 1) firstKeyIndex = 1;',
	'}',
	'var loopStartTime = key(firstKeyIndex).time;',
	'var loopEndTime = key(lastKey.index).time;',
	'var loopDuration = loopEndTime - loopStartTime;',
	'if (t <= loopEndTime) return currentValue;',
	'var timeSpent = t - loopEndTime;',
	'var numLoops = Math.floor(timeSpent / loopDuration);',
	'var loopTime = timeSpent;',
	'if (numLoops > 0) loopTime = timeSpent - numLoops * loopDuration;',
	'var r = vAtTime(loopStartTime + loopTime);',
	'if (o) r += (key(lastKey.index).value - key(firstKeyIndex).value) * (numLoops + 1);',
	'var damp = Math.exp(timeSpent * damping);',
	'return (r-currentValue) / damp + currentValue;',
	'}'
	].join('\n');
DuAEExpression.Library["cycleOut"].requirements = ["getPrevKey"];

/**
 * Overshoot animation, to be used when the speed is 0.
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all subsequent frames.
 * @param {float} elasticity The elasticity, which controls the amplitude and frequence according to the last known velocity
 * @param {float} damping Damping
 * @param {function} [vAtTime=valueAtTime] A function to replace valueAtTime. Use this to loop after an expression+keyframe controlled animation, by providing a function used to generate the animation.
 * @returns {float|float[]} The new value
 * @function
 * @requires getPrevKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["overshoot"] = {};
DuAEExpression.Library["overshoot"].expression = ['function overShoot(t, elasticity, damping, vAtTime) {',
	'if (elasticity == 0) return value;',
	'if (numKeys < 2) return value;',
	'if (nearestKey(t).index == 1) return value;',
	'var pVelocity = ( vAtTime(t) - vAtTime( t - .01 ) ) * 100;',
	'var pSpeed = length( pVelocity );',
	'if (pSpeed >= 0.001 ) return value;',
	'var oShootStart = 0;',
	'var oShootTime = 0;',
	'var oShootKey = getPrevKey(t, thisProperty);',
	'oShootStart = oShootKey.time;',
	'oShootTime = t - oShootStart;',
	'pVelocity = ( vAtTime( oShootStart ) - vAtTime( oShootStart - thisComp.frameDuration * .5 ) );',
	'var damp = Math.exp(oShootTime * damping);',
	'var sinus = elasticity * oShootTime * 2 * Math.PI;',
	'sinus = Math.sin(sinus);',
	'sinus = (.3 / elasticity) * sinus;',
	'sinus = sinus / damp;',
	'if (Math.abs(sinus) < .00001) return value;',
	'var oShoot = ( pVelocity / thisComp.frameDuration ) * sinus;',
	'return oShoot+value;',
	'}'
	].join('\n');
DuAEExpression.Library["overshoot"].requirements = ["getPrevKey"];

/**
 * Animatable equivalent to loopIn('pingpong').
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all previous frames.
 * @param {int} nK The number of keyframes to loop. Use 0 to loop all keyframes
 * @param {function} [vAtTime=valueAtTime] A function to replace valueAtTime. Use this to loop after an expression+keyframe controlled animation, by providing a function used to generate the animation.
 * @param {float} [damping=0] Exponentially attenuates the effect over time
 * @returns {float} The new value
 * @function
 * @requires getNextKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["pingPongIn"] = {};
DuAEExpression.Library["pingPongIn"].expression = ['function pingPongIn(t, nK, vAtTime, damping) {',
	'var currentValue = vAtTime(t);',
	'if (numKeys <= 1) return currentValue;',
	'var lastKeyIndex = numKeys;',
	'var firstKey = getNextKey(t, thisProperty);',
	'if (!firstKey) return currentValue;',
	'if (firstKey.index == lastKeyIndex) return currentValue;',
	'if (nK >= 2) {',
	'nK = nK - 1;',
	'lastKeyIndex = firstKey.index + nK;',
	'if (lastKeyIndex > numKeys) lastKeyIndex = numKeys;',
	'}',
	'var loopStartTime = firstKey.time;',
	'var loopEndTime = key(lastKeyIndex).time;',
	'var loopDuration = loopEndTime - loopStartTime;',
	'if (t >= loopStartTime) return currentValue;',
	'var timeSpent = loopStartTime - t;',
	'var numLoops = Math.floor(timeSpent / loopDuration);',
	'var loopTime = timeSpent;',
	'if (numLoops > 0) {',
	'loopTime = timeSpent - numLoops * loopDuration;',
	'if (numLoops % 2 != 0) loopTime = loopDuration - loopTime;',
	'}',
	'var damp = Math.exp(timeSpent * damping);',
	'return ( vAtTime(loopStartTime + loopTime) - currentValue) / damp + currentValue;',
	'}'
	].join('\n');
DuAEExpression.Library["pingPongIn"].requirements = ["getNextKey"];

/**
 * Animatable equivalent to loopOut('pingpong').
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @param {float} t The time at which the value must be got. To end the loop, pass the same time for all subsequent frames.
 * @param {int} nK The number of keyframes to loop. Use 0 to loop all keyframes
 * @param {function} [vAtTime=valueAtTime] A function to replace valueAtTime. Use this to loop after an expression+keyframe controlled animation, by providing a function used to generate the animation.
 * @param {float} [damping=0] Exponentially attenuates the effect over time
 * @returns {float} The new value
 * @function
 * @requires getPrevKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["pingPongOut"] = {};
DuAEExpression.Library["pingPongOut"].expression = ['function pingPongOut(t, nK, vAtTime, damping) {',
	'var currentValue = vAtTime(t);',
	'if (numKeys <= 1) return currentValue;',
	'var firstKeyIndex = 1;',
	'var lastKey = getPrevKey(t, thisProperty);',
	'if (!lastKey) return currentValue;',
	'if (lastKey.index == firstKeyIndex) return currentValue;',
	'if (nK >= 2) {',
	'nK = nK - 1;',
	'firstKeyIndex = lastKey.index - nK;',
	'if (firstKeyIndex < 1) firstKeyIndex = 1;',
	'}',
	'var loopStartTime = key(firstKeyIndex).time;',
	'var loopEndTime = key(lastKey.index).time;',
	'var loopDuration = loopEndTime - loopStartTime;',
	'if (t <= loopEndTime) return currentValue;',
	'var timeSpent = t - loopEndTime;',
	'var numLoops = Math.floor(timeSpent / loopDuration);',
	'var loopTime = loopDuration - timeSpent;',
	'if (numLoops > 0) {',
	'loopTime = timeSpent - numLoops * loopDuration;',
	'if (numLoops % 2 == 0) loopTime = loopDuration - loopTime;',
	'}',
	'var damp = Math.exp(timeSpent * damping);',
	'return ( vAtTime(loopStartTime + loopTime) - currentValue) / damp + currentValue;',
	'}'
	].join('\n');
DuAEExpression.Library["pingPongOut"].requirements = ["getPrevKey"];

/**
 * Adds two lists of points/vectors.
 * @function
 * @param {float[][]} p1 The list of points
 * @param {float[][]} p2 The other list of points
 * @param {float} w A weight to multiply the values of p2
 * @returns {float[][]} The added points
 * @category ExpressionLibrary
 */
DuAEExpression.Library["addPoints"] = {};
DuAEExpression.Library["addPoints"].expression = ['function addPoints(p1, p2, w) {',
	'var n = p1.length;',
	'if (p2.length > n) n = p2.length;',
	'var r = [];',
	'for (var i = 0; i < n; i++) {',
	'if (i >= p1.length) {',
	'r.push(p2[i] * w);',
	'continue;',
	'}',
	'if (i >= p2.length) {',
	'r.push(p1[i]);',
	'continue;',
	'}',
	'r.push(p1[i] + p2[i] * w);',
	'}',
	'return r;',
	'}'
	].join('\n');
DuAEExpression.Library["addPoints"].requirements = [];

/**
    * Fix for the ExtendScript engine, implements the Math.cbrt (cubic root) function.
    * @function
    * @name Math.cbrt
    * @param {Number} x The value
    * @return {Number} The cubic root
    * @category ExpressionLibrary
    */
DuAEExpression.Library["cbrt"] = {};
DuAEExpression.Library["cbrt"].expression = ['if (typeof Math.cbrt === \'undefined\') {',
	'Math.cbrt = (function(pow) {',
	'return function cbrt(x){',
	'return x < 0 ? -pow(-x, 1/3) : pow(x, 1/3);',
	'};',
	'})(Math.pow);',
	'}'
	].join('\n');
DuAEExpression.Library["cbrt"].requirements = [];

/**
    * Gets the distance of a point to a line
    * @function
    * @param {float[]} point The point [x,y]
    * @param {float[][]} line The line [ A , B ] where A and B are two points
    * @return {float} The distance
    * @category ExpressionLibrary
    */
DuAEExpression.Library["distanceToLine"] = {};
DuAEExpression.Library["distanceToLine"].expression = ['function distanceToLine( point, line ) {',
	'var b = line[0];',
	'var c = line [1];',
	'var a = point;',
	'var line = Math.pow( length( b, c ), 2 );',
	'if ( line === 0 ) return Math.pow( length( a, b ), 2 );',
	'var d = ( ( a[ 0 ] - b[ 0 ] ) * ( c[ 0 ] - b[ 0 ] ) + ( a[ 1 ] - b[ 1 ] ) * ( c[ 1 ] - b[ 1 ] ) ) / line;',
	'd = Math.max( 0, Math.min( 1, d ) );',
	'var distance = Math.pow( length( a, [ b[ 0 ] + d * ( c[ 0 ] - b[ 0 ] ), b[ 1 ] + d * ( c[ 1 ] - b[ 1 ] ) ] ), 2 );',
	'return Math.sqrt( distance );',
	'};'
	].join('\n');
DuAEExpression.Library["distanceToLine"].requirements = [];

/**
    * The gaussian function
    * @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
    * @category ExpressionLibrary
    */
DuAEExpression.Library["gaussian"] = {};
DuAEExpression.Library["gaussian"].expression = ['function gaussian( value, min, max, center, fwhm)',
	'{',
	'if (typeof min === \'undefined\') min = 0;',
	'if (typeof max === \'undefined\') max = 1;',
	'if (typeof center === \'undefined\') center = 0;',
	'if (typeof fwhm === \'undefined\') 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;',
	'}'
	].join('\n');
DuAEExpression.Library["gaussian"].requirements = [];

/**
    * The inverse gaussian function
    * @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
    * @category ExpressionLibrary
    */
DuAEExpression.Library["inverseGaussian"] = {};
DuAEExpression.Library["inverseGaussian"].expression = ['function inverseGaussian ( v, min, max, center, fwhm)',
	'{',
	'if (typeof min === \'undefined\') min = 0;',
	'if (typeof max === \'undefined\') max = 1;',
	'if (typeof center === \'undefined\') center = 0;',
	'if (typeof fwhm === \'undefined\') 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 ];',
	'}'
	].join('\n');
DuAEExpression.Library["inverseGaussian"].requirements = [];

/**
    * The inverse logistic function (inverse sigmoid)
    * @function
    * @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
    * @category ExpressionLibrary
    */
DuAEExpression.Library["inverseLogistic"] = {};
DuAEExpression.Library["inverseLogistic"].expression = ['function inverseLogistic ( v, midValue, min, max, rate)',
	'{',
	'if (typeof midValue === \'undefined\') midValue = 0;',
	'if (typeof min === \'undefined\') min = 0;',
	'if (typeof max === \'undefined\') max = 1;',
	'if (typeof rate === \'undefined\') rate = 1;',
	'if (v == min) return 0;',
	'return midValue - Math.log( (max-min)/(v-min) - 1) / rate;',
	'}'
	].join('\n');
DuAEExpression.Library["inverseLogistic"].requirements = [];

/**
    * Checks if the value is 0; works with arrays.
    * @function
    * @param {Number|Number[]} x The value(s)
    * @return {Boolean} true if all values are 0.
    * @category ExpressionLibrary
    */
DuAEExpression.Library["isZero"] = {};
DuAEExpression.Library["isZero"].expression = ['function isZero(a)',
	'{',
	'if (a.length)',
	'{',
	'for ( var i = 0; i < a.length; i++ )',
	'{',
	'if ( a[i] != 0 ) return false;',
	'}',
	'}',
	'else if (a != 0) return false;',
	'return true;',
	'}'
	].join('\n');
DuAEExpression.Library["isZero"].requirements = [];

/**
    * The logistic function (sigmoid)
    * @function
    * @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)
    * @category ExpressionLibrary
    */
DuAEExpression.Library["logistic"] = {};
DuAEExpression.Library["logistic"].expression = ['function logistic( value, midValue, min, max, rate)',
	'{',
	'if (typeof midValue === \'undefined\') midValue = 0;',
	'if (typeof min === \'undefined\') min = 0;',
	'if (typeof max === \'undefined\') max = 1;',
	'if (typeof rate === \'undefined\') rate = 1;',
	'var exp = -rate*(value - midValue);',
	'var result = 1 / (1 + Math.pow(Math.E, exp));',
	'return result * (max-min) + min;',
	'}'
	].join('\n');
DuAEExpression.Library["logistic"].requirements = [];

/**
    * Returns the mean of a set of values
    * @function
    * @param {Number[]} values The values
    * @return {Number} The mean
    * @category ExpressionLibrary
    */
DuAEExpression.Library["mean"] = {};
DuAEExpression.Library["mean"].expression = ['function mean( values )',
	'{',
	'var num = values.length;',
	'var result = 0;',
	'for (var i = 0; i < num; i++)',
	'{',
	'result += values[i];',
	'}',
	'return result / num;',
	'}'
	].join('\n');
DuAEExpression.Library["mean"].requirements = [];

/**
 * Multiplies a list of points/vectors with a scalar.
 * @function
 * @param {float[][]} p The list of points
 * @param {float} w The multiplier
 * @returns {float[][]} The multiplied points
 * @category ExpressionLibrary
 */
DuAEExpression.Library["multPoints"] = {};
DuAEExpression.Library["multPoints"].expression = ['function multPoints(p, w) {',
	'var r = [];',
	'for (var i = 0, n = p.length; i < n; i++) {',
	'r.push(p[i] * w);',
	'}',
	'return r;',
	'}'
	].join('\n');
DuAEExpression.Library["multPoints"].requirements = [];

/**
 * Normalizes a list of weights so their sum equals 1.0
 * @function
 * @param {float[]} weights The weights to normalize
 * @param {float} [sum] The sum of the weights; provide it if it's already computed to improve performance.
 * @returns {float[]} The normalized weights
 * @category ExpressionLibrary
 */
DuAEExpression.Library["normalizeWeights"] = {};
DuAEExpression.Library["normalizeWeights"].expression = ['function normalizeWeights(weights, sum) {',
	'if(typeof sum === \'undefined\') {',
	'sum = 0;',
	'for (var i = 0, n = weights.length; i < n; i++) {',
	'sum += weights[i];',
	'}',
	'}',
	'if (sum == 1 || sum == 0) return weights;',
	'var o = 1 - sum;',
	'var normalized = [];',
	'for (var i = 0, n = weights.length; i < n; i++) {',
	'var w = weights[i];',
	'normalized.push(w + (w / sum) * o);',
	'}',
	'return normalized;',
	'}'
	].join('\n');
DuAEExpression.Library["normalizeWeights"].requirements = [];

/**
    * Fix for the ExtendScript engine, implements the Math.sign function.
    * @function
    * @name Math.sign
    * @param {Number} x The value
    * @return {Number} The sign, 1, -1 or 0.
    * @category ExpressionLibrary
    */
DuAEExpression.Library["sign"] = {};
DuAEExpression.Library["sign"].expression = ['if (typeof Math.sign === \'undefined\') Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; };'
	].join('\n');
DuAEExpression.Library["sign"].requirements = [];

/**
 * Substracts two lists of points/vectors.
 * @function
 * @param {float[][]} p1 The list of points
 * @param {float[][]} p2 The other list of points
 * @param {float} w A weight to multiply the values of p2
 * @returns {float[][]} The substracted points
 * @category ExpressionLibrary
 */
DuAEExpression.Library["subPoints"] = {};
DuAEExpression.Library["subPoints"].expression = ['function subPoints(p1, p2, w) {',
	'var n = p1.length;',
	'if (p2.length > n) n = p2.length;',
	'var r = [];',
	'for (var i = 0; i < n; i++) {',
	'if (i >= p1.length) {',
	'r.push(-p2[i] * w);',
	'continue;',
	'}',
	'if (i >= p2.length) {',
	'r.push(p1[i]);',
	'continue;',
	'}',
	'r.push(p1[i] - p2[i] * w);',
	'}',
	'return r;',
	'}'
	].join('\n');
DuAEExpression.Library["subPoints"].requirements = [];

/**
 * Adds two paths together.<br />
 * The paths must be objects with three array attributes: points, inTangents, outTangents
 * @function
 * @param {Object} path1 First path
 * @param {Object} path2 Second path
 * @param {float} path2weight A weight to multiply the second path values
 * @returns {Object} A path object with three array attributes: points, inTangents, outTangents
 * @requires addPoints
 * @category ExpressionLibrary
 */
DuAEExpression.Library["addPath"] = {};
DuAEExpression.Library["addPath"].expression = ['function addPath(path1, path2, path2weight) {',
	'var vertices = addPoints(path1.points, path2.points, path2weight);',
	'var inT = addPoints(path1.inTangents, path2.inTangents, path2weight);',
	'var outT = addPoints(path1.outTangents, path2.outTangents, path2weight);',
	'var path = {};',
	'path.points = vertices;',
	'path.inTangents = inT;',
	'path.outTangents = outT;',
	'return path;',
	'}'
	].join('\n');
DuAEExpression.Library["addPath"].requirements = ["addPoints"];

/**
 * Checks if a point is inside a given polygon.
 * @function
 * @param {float[]} point A 2D point [x, y]
 * @param {float[][]} points The vertices of the polygon
 * @returns {object} An object with two properties:  
 * - `inside (bool)` is true if the point is inside the polygon
 * - `closestVertex` is the index of the closest vertex of the polygon
 * @category ExpressionLibrary
 */
DuAEExpression.Library["inside"] = {};
DuAEExpression.Library["inside"].expression = ['function inside( point, points ) {',
	'var x = point[ 0 ],',
	'y = point[ 1 ];',
	'var result = 0;',
	'var inside = false;',
	'for ( var i = 0, j = points.length - 1; i < points.length; j = i++ ) {',
	'var xi = points[ i ][ 0 ],',
	'yi = points[ i ][ 1 ];',
	'var xj = points[ j ][ 0 ],',
	'yj = points[ j ][ 1 ];',
	'var intersect = ( ( yi > y ) != ( yj > y ) ) &&',
	'( x <',
	'( xj - xi ) * ( y - yi ) / ( yj - yi ) + xi );',
	'if ( intersect ) inside = !inside;',
	'var t1 = length( points[ i ], point );',
	'var t2 = length( points[ result ], point );',
	'if ( t1 < t2 ) {',
	'result = i;',
	'}',
	'}',
	'return { inside: inside, closestVertex: result };',
	'};'
	].join('\n');
DuAEExpression.Library["inside"].requirements = [];

/**
 * Multiplies a path with a scalar.<br />
 * The path must be an object with three array attributes: points, inTangents, outTangents
 * @function
 * @param {Object} path The path
 * @param {float} weight The multipliers
 * @returns {Object} A path object with three array attributes: points, inTangents, outTangents
 * @requires multPoints
 * @category ExpressionLibrary
 * @category ExpressionLibrary
 */
DuAEExpression.Library["multPath"] = {};
DuAEExpression.Library["multPath"].expression = ['function multPath(path, weight) {',
	'var vertices = multPoints(path.points, weight);',
	'var inT = multPoints(path.inTangents, weight);',
	'var outT = multPoints(path.outTangents, weight);',
	'var path = {};',
	'path.points = vertices;',
	'path.inTangents = inT;',
	'path.outTangents = outT;',
	'return path;',
	'}'
	].join('\n');
DuAEExpression.Library["multPath"].requirements = ["multPoints"];

/**
 * Substracts two paths together.<br />
 * The paths must be objects with three array attributes: points, inTangents, outTangents
 * @function
 * @param {Object} path1 First path
 * @param {Object} path2 Second path
 * @param {float} path2weight A weight to multiply the second path values
 * @returns {Object} A path object with three array attributes: points, inTangents, outTangents
 * @requires subPoints
 * @category ExpressionLibrary
 */
DuAEExpression.Library["subPath"] = {};
DuAEExpression.Library["subPath"].expression = ['function subPath(path1, path2, path2weight) {',
	'var vertices = subPoints(path1.points, path2.points, path2weight);',
	'var inT = subPoints(path1.inTangents, path2.inTangents, path2weight);',
	'var outT = subPoints(path1.outTangents, path2.outTangents, path2weight);',
	'var path = {};',
	'path.points = vertices;',
	'path.inTangents = inT;',
	'path.outTangents = outT;',
	'return path;',
	'}'
	].join('\n');
DuAEExpression.Library["subPath"].requirements = ["subPoints"];

/**
 * Checks the type of a pseudo-effect used by Duik.<br />
 * This is a workaround for the missing matchName in expressions.<br />
 * Pseudo-Effects used by Duik start with a hidden property which name is the same as the matchName of the effect itself (without the 'Pseudo/' part).
 * @function
 * @example
 * if ( checkEffect(thisLayer.effect(1), "DUIK parentConstraint2") ) { "This is the second version of the parent constraint by Duik" }
 * else { "Who knows what this is?" }
 * @param {Property} fx The effect to check
 * @param {string} duikMatchName The matchName of a pseudo-effect used by Duik (without the 'Pseudo/' part)
 * @return {boolean} True when the property at propIndex is named propName
 * @category ExpressionLibrary
 */
DuAEExpression.Library["checkDuikEffect"] = {};
DuAEExpression.Library["checkDuikEffect"].expression = ['function checkDuikEffect(fx, duikMatchName) {',
	'if (fx.numProperties  < 3) return false;',
	'try { if (fx(2).name != duikMatchName) return false; }',
	'catch (e) { return false; }',
	'return true;',
	'}'
	].join('\n');
DuAEExpression.Library["checkDuikEffect"].requirements = [];

/**
 * Checks the type of an effect.<br />
 * This is a workaround for the missing matchName in expressions.<br />
 * It checks if the given effect has a specific property at a specific index.
 * @function
 * @example
 * if ( checkEffect(thisLayer.effect(1), 1, "Blur") ) { "The first effect is a blur!" }
 * else { "Who knows what this is?" }
 * @param {Property} fx The effect to check
 * @param {int} propIndex The index of the property
 * @param {string} propName The expected name of the property. Be careful with the internationalization of After Effects...
 * @return {boolean} True when the property at propIndex is named propName
 * @category ExpressionLibrary
 */
DuAEExpression.Library["checkEffect"] = {};
DuAEExpression.Library["checkEffect"].expression = ['function checkEffect(fx, propIndex, propName) {',
	'if (fx.numProperties  < propIndex) return false;',
	'if (!!$.engineName) {',
	'if ( fx(propIndex).name != propName ) return false;',
	'}',
	'else {',
	'try { if (fx(propIndex).name != propName) return false; }',
	'catch (e) { return false; }',
	'}',
	'return true;',
	'}'
	].join('\n');
DuAEExpression.Library["checkEffect"].requirements = [];

/**
 * Gets a layer from a layer property in an effect, without generating an error if "None" is selected with the Legacy expression engine.
 * @function
 * @param {Property} fx The effect
 * @param {int|string} ind The index or the name of the property
 * @return {Layer|null} The layer, or null if set to "None"
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getEffectLayer"] = {};
DuAEExpression.Library["getEffectLayer"].expression = ['function getEffectLayer( fx, ind ) {',
	'try { var l = fx( ind ); return l; }',
	'catch ( e ) { return null; }',
	'}'
	].join('\n');
DuAEExpression.Library["getEffectLayer"].requirements = [];

/**
 * Gets the path from the current property at a given time.
 * @function
 * @return {Object} A path object with three array attributes: points, inTangents, outTangents
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPath"] = {};
DuAEExpression.Library["getPath"].expression = ['function getPath(t) {',
	'var path = {};',
	'path.points = points(t);',
	'path.inTangents = inTangents(t);',
	'path.outTangents = outTangents(t);',
	'return path;',
	'}'
	].join('\n');
DuAEExpression.Library["getPath"].requirements = [];

/**
 * Gets a property from an array of indices as returned by getPropPath.
 * @function
 * @param {Layer} l The layer containing the needed property
 * @param {int[]} p The indices to the property.
 * @return {Property} The property.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPropFromPath"] = {};
DuAEExpression.Library["getPropFromPath"].expression = ['function getPropFromPath( l, p )',
	'{',
	'prop = l;',
	'for ( var i = p.length - 1; i >= 0; i-- )',
	'prop = prop(p[i]);',
	'return prop;',
	'}'
	].join('\n');
DuAEExpression.Library["getPropFromPath"].requirements = [];

/**
 * Gets an array of all indices needed to get the current property from the layer level.
 * @function
 * @return {int[]} All indices to the property.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPropPath"] = {};
DuAEExpression.Library["getPropPath"].expression = ['function getPropPath()',
	'{',
	'var path = [];',
	'var prop = thisProperty;',
	'ok = true;',
	'while( ok )',
	'{',
	'try {',
	'path.push( prop.propertyIndex );',
	'prop = prop.propertyGroup();',
	'}',
	'catch (e) { ok = false; }',
	'}',
	'return path;',
	'}'
	].join('\n');
DuAEExpression.Library["getPropPath"].requirements = [];

/**
 * Gets the same property as the current one but from another layer.
 * @function
 * @param {Layer} l The layer containing the needed property
 * @return {Property} The property.
 * @requires getPropFromPath
 * @requires getPropPath
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getSameProp"] = {};
DuAEExpression.Library["getSameProp"].expression = ['function getSameProp( l )',
	'{',
	'return getPropFromPath( l, getPropPath() );',
	'}'
	].join('\n');
DuAEExpression.Library["getSameProp"].requirements = ["getPropFromPath","getPropPath"];

/**
 * Checks if a property is a layer. Useful when traversing up the property tree to stop when getting the layer.
 * @function
 * @param {Property} prop - The property to test
 * @return {boolean} true if the prop is a layer
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isLayer"] = {};
DuAEExpression.Library["isLayer"].expression = ['function isLayer( prop ) {',
	'try { if ( prop.index ) return true; }',
	'catch (e) { return false; }',
	'}'
	].join('\n');
DuAEExpression.Library["isLayer"].requirements = [];

/**
 * Checks if a property is a path property.
 * @function
 * @param {Property} prop The property
 * @return {boolean} true if the property is a path property.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isPath"] = {};
DuAEExpression.Library["isPath"].expression = ['function isPath(prop) {',
	'try {',
	'createPath();',
	'return true;',
	'} catch (e) {',
	'return false;',
	'}',
	'}'
	].join('\n');
DuAEExpression.Library["isPath"].requirements = [];

/**
 * Checks if a property is a transform.position property.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {Property} [prop=thisProperty] The property
 * @return {boolean} true if the property is the transform.position property.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isPosition"] = {};
DuAEExpression.Library["isPosition"].expression = ['function isPosition(prop) {',
	'return  prop === position;',
	'}'
	].join('\n');
DuAEExpression.Library["isPosition"].requirements = [];

/**
 * Checks if a property is spatial
 * @function
 * @param {Property} [prop=thisProperty] The property to check
 * @return {boolean} true if the property is spatial.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isSpatial"] = {};
DuAEExpression.Library["isSpatial"].expression = ['function isSpatial(prop) {',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'if (!(prop.value instanceof Array)) return false;',
	'if (prop.value.length != 2 && prop.value.length != 3) return false;',
	'try { if (typeof prop.speed !== "undefined") return true; }',
	'catch (e) { return false; }',
	'}'
	].join('\n');
DuAEExpression.Library["isSpatial"].requirements = [];

/**
 * Checks if the current property is animated at a given time.
 * @function
 * @param {number} [t=time] The time
 * @param {number} [threshold=0.01] The speed under which the property is considered still.
 * @param {number} [axis=-1] The axis to check. If < 0, will check all axis.
 * @return {boolean} true if the property does not vary.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["isStill"] = {};
DuAEExpression.Library["isStill"].expression = ['function isStill(t, threshold, axis) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof threshold === \'undefined\') threshold = 0.01;',
	'if (typeof axis === \'undefined\') axis = -1;',
	'var d = valueAtTime(t) - valueAtTime(t + thisComp.frameDuration*.1);',
	'if (d instanceof Array) {',
	'if (axis >= 0) return Math.abs(d[axis]) <= threshold;',
	'for (var i = 0; i < d.length; i++) {',
	'if (Math.abs(d[i]) > threshold) return false;',
	'}',
	'return true;',
	'} else return Math.abs(d) <= threshold;',
	'}'
	].join('\n');
DuAEExpression.Library["isStill"].requirements = [];

/**
 * Checks the last previous time at which the property value was not 0. (meant to be used on boolean property, works on single dimension properties too).
 * @param {Property} prop The property to check
 * @param {float} t The time before which to run the check
 * @returns {float} The last active time before t
 * @function
 * @category ExpressionLibrary
 * @requires getPrevKey
 */
DuAEExpression.Library["lastActiveTime"] = {};
DuAEExpression.Library["lastActiveTime"].expression = ['function lastActiveTime( prop, t ) {',
	'if ( prop.valueAtTime(t) ) return t;',
	'while( t > 0 )',
	'{',
	'var prevKey = getPrevKey( t, prop );',
	'if ( !prevKey ) break;',
	'if ( prevKey.value ) return t;',
	't = prevKey.time - .001;',
	'}',
	'return 0;',
	'}'
	].join('\n');
DuAEExpression.Library["lastActiveTime"].requirements = ["getPrevKey"];

/**
 * Checks the next time at which the property value was not 0. (meant to be used on boolean property, works on single dimension properties too).
 * @param {Property} prop The property to check
 * @param {float} t The time after which to run the check
 * @returns {float} The next active time after t
 * @function
 * @requires getNextKey
 * @category ExpressionLibrary
 */
DuAEExpression.Library["nextActiveTime"] = {};
DuAEExpression.Library["nextActiveTime"].expression = ['function nextActiveTime( prop, t )',
	'{',
	'if ( prop.valueAtTime(t) ) return t;',
	'while( t < thisComp.duration )',
	'{',
	'var nextKey = getNextKey( t + .001, prop );',
	'if ( !nextKey ) break;',
	't = nextKey.time;',
	'if ( nextKey.value ) return t;',
	'}',
	'return 0;',
	'}'
	].join('\n');
DuAEExpression.Library["nextActiveTime"].requirements = ["getNextKey"];

/**
 * Generates a "zero" value for the current property, i.e. <code>0</code> or <code>[0,0]</code>, etc. according to the property type.<br />
 * Note that for path properties, this method returns a path object with three array attributes: points, inTangents, outTangents.
 * @function
 * @return {any} The zero value.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["zero"] = {};
DuAEExpression.Library["zero"].expression = ['function zero() {',
	'if (typeof thisProperty.value === "number") return 0;',
	'if (thisProperty.value instanceof Array) {',
	'var result = [0];',
	'for (var i = 1, n = value.length; i < n; i++) result.push(0);',
	'return result;',
	'}',
	'if (isPath(thisProperty)) {',
	'var path = {};',
	'var vertices = [',
	'[0, 0]',
	'];',
	'var inT = [',
	'[0, 0]',
	'];',
	'var outT = [',
	'[0, 0]',
	'];',
	'for (var i = 1, n = points(0).length; i < n; i++) {',
	'vertices.push([0, 0]);',
	'inT.push([0, 0]);',
	'outT.push([0, 0]);',
	'}',
	'path.points = vertices;',
	'path.inTangents = inT;',
	'path.outTangents = outT;',
	'return path;',
	'}',
	'}'
	].join('\n');
DuAEExpression.Library["zero"].requirements = [];

/**
 * Adds some noise to a value.<br />
 * You may use seedRandom() before using this function as it will influence the generated noise.
 * A timeless noise can be achieved with <code>seedRandom(index,true)</code> for example.
 * @function
 * @param {number|number[]} val The value to add noise to.
 * @param {float} quantity The quantity of noise. A percentage. 100 means the value can range from (val * 0) to (val * 2).
 * @example
 * seedRandom(index, true) // a timeless noise
 * addNoise(value, 50 ); // the property value will have noise between (value * 0.5) and (value * 1.5) which won't vary through time.
 * @example
 * seedRandom(index, false);
 * addNoise(value, 33 ); // the noise will change at each frame, varying between (value * .66) and (value * 1.33)
 * @category ExpressionLibrary
 */
DuAEExpression.Library["addNoise"] = {};
DuAEExpression.Library["addNoise"].expression = ['function addNoise( val, quantity ) {',
	'var randomValue = random(0.9,1.1);',
	'var noiseValue = noise(valueAtTime(0) * randomValue);',
	'noiseValue = noiseValue * (quantity / 100);',
	'return val * ( noiseValue + 1 );',
	'}'
	].join('\n');
DuAEExpression.Library["addNoise"].requirements = [];

/**
 * Removes the ancestors rotation from the rotation of a layer.
 * This is very useful to make a layer keep its orientation without being influenced by its parents.<br />
 * @function
 * @example
 * //in a rotation property, just include the function and use:
 * dishineritRotation();
 * //the layer will now keep its own orientation.
 * @example
 * //you can also combine the result with something else
 * var result = dishineritRotation();
 * result + wiggle(5,20);
 * @function
 * @param {Layer} [l=thisLayer] The layer
 * @return {float} The new rotation value, in degrees.
 * @requires sign
 * @category ExpressionLibrary
 */
DuAEExpression.Library["dishineritRotation"] = {};
DuAEExpression.Library["dishineritRotation"].expression = ['function dishineritRotation( l ) {',
	'if (typeof l === \'undefined\') l = thisLayer;',
	'var r = l.rotation.value;',
	'while ( l.hasParent ) {',
	'l = l.parent;',
	'var s = l.scale.value;',
	'r -= l.rotation.value * Math.sign(s[0]*s[1]);',
	'}',
	'return r;',
	'}'
	].join('\n');
DuAEExpression.Library["dishineritRotation"].requirements = ["sign"];

/**
 * Removes the ancestors scale from the scale of a layer.
 * This is very useful to make a layer keep its scale without being influenced by its parents.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @example
 * //in a rotation property, just include the function and use:
 * dishineritScale();
 * //the layer will now keep its own scale.
 * @example
 * //you can also combine the result with something else
 * var result = dishineritScale();
 * result + wiggle(5,20);
 * @function
 * @param {Layer} [l=thisLayer] The layer
 * @return {float[]} The new scale value, in percent.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["dishineritScale"] = {};
DuAEExpression.Library["dishineritScale"].expression = ['function dishineritScale( l ) {',
	'var s = l.scale.value;',
	'var threeD = s.length == 3;',
	'while ( l.hasParent ) {',
	'l = l.parent;',
	'var ps = l.scale.value / 100;',
	'if (threeD && ps.length == 3) {',
	's = [ s[0]/ps[0], s[1]/ps[1], s[2]/ps[2] ];',
	'}',
	'else if (threeD) {',
	's = [ s[0]/ps[0], s[1]/ps[1], s[2] ];',
	'}',
	'else {',
	's = [ s[0]/ps[0], s[1]/ps[1] ];',
	'}',
	'}',
	'return s;',
	'}'
	].join('\n');
DuAEExpression.Library["dishineritScale"].requirements = [];

/**
 * Converts the point coordinates from the current group in the shape layer to the Layer.<br />
 * Use toWorld and toComp with the result if you need te coordinates in the world or the comp.
 * @function
 * @param {number[]} point The 2D coordinates of the point in the current group.
 * @return {number[]} The 2D coordinates of the point in the Layer.
 * @requires Matrix
 * @requires getGroupTransformMatrix
 * @category ExpressionLibrary
 */
DuAEExpression.Library["fromGroupToLayer"] = {};
DuAEExpression.Library["fromGroupToLayer"].expression = ['function fromGroupToLayer( point ) {',
	'var matrix = getGroupTransformMatrix();',
	'return matrix.applyToPoint( point );',
	'}'
	].join('\n');
DuAEExpression.Library["fromGroupToLayer"].requirements = ["Matrix","getGroupTransformMatrix"];

/**
 * Converts the point coordinates from Layer to the current group in the shape layer.<br />
 * Use fromWorld or fromComp first if you need to convert from the world or composition coordinates, and pass the result to this function.
 * @function
 * @param {number[]} point The 2D coordinates of the point in the Layer.
 * @return {number[]} The 2D coordinates of the point in the current group.
 * @requires Matrix
 * @requires getGroupTransformMatrix
 * @category ExpressionLibrary
 */
DuAEExpression.Library["fromLayerToGroup"] = {};
DuAEExpression.Library["fromLayerToGroup"].expression = ['function fromLayerToGroup( point ) {',
	'var matrix = getGroupTransformMatrix().inverse();',
	'return matrix.applyToPoint( point );',
	'}'
	].join('\n');
DuAEExpression.Library["fromLayerToGroup"].requirements = ["Matrix","getGroupTransformMatrix"];

/**
 * Gets the "real" scale of a layer, resulting to its scale property, the scale of its parents, and it's location in Z-space if it's 3D.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {Layer} [l=thisLayer] The layer 
 * @param {number} [t=time] The time when to get the scale
 * @return {number} The scale ratio. This is not a percent, 1.0 is 100%.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getCompScale"] = {};
DuAEExpression.Library["getCompScale"].expression = ['function getCompScale( l, t ) {',
	'var originalWidth = length( l.anchorPoint, [ l.width, 0 ] );',
	'var anchorInComp = l.toComp( l.anchorPoint, t );',
	'var widthInComp = l.toComp( [ l.width, 0 ], t );',
	'var newWidth = length(anchorInComp, widthInComp);',
	'return newWidth / originalWidth;',
	'}'
	].join('\n');
DuAEExpression.Library["getCompScale"].requirements = [];

/**
 * Gets the transformation Matrix for the current group in a shape layer, including the transformation from the ancestor groups<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {Property} [prop=thisProperty] The property from which to get the matrix
 * @return {Matrix} The 2D Transform Matrix.
 * @requires isLayer
 * @requires Matrix
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getGroupTransformMatrix"] = {};
DuAEExpression.Library["getGroupTransformMatrix"].expression = ['function getGroupTransformMatrix( prop ) {',
	'var matrix = new Matrix();',
	'var shapeGroups = [];',
	'var parentProp = prop.propertyGroup(1);',
	'while( parentProp && !isLayer(parentProp) )',
	'{',
	'try { if ( parentProp.transform ) shapeGroups.push( parentProp.transform ); }',
	'catch (e) {}',
	'parentProp = parentProp.propertyGroup(1);',
	'}',
	'for (var i = shapeGroups.length - 1; i >= 0; i--)',
	'{',
	'var group = shapeGroups[i];',
	'matrix.translate( group.position.value );',
	'matrix.rotate( group.rotation.value );',
	'var aPX = -( group.anchorPoint.value[ 0 ] * group.scale.value[ 0 ] / 100 );',
	'var aPY = -( group.anchorPoint.value[ 1 ] * group.scale.value[ 1 ] / 100 );',
	'matrix.translate( [ aPX, aPY ] );',
	'matrix.scale( group.scale.value / 100 );',
	'}',
	'return matrix;',
	'}'
	].join('\n');
DuAEExpression.Library["getGroupTransformMatrix"].requirements = ["isLayer","Matrix"];

/**
 * Gets the comp position (2D Projection in the comp) of a layer.
 * @function
 * @param {number} [t=time] Time from when to get the position
 * @param {Layer} [l=thisLayer] The layer
 * @return {number[]} The comp position
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getLayerCompPos"] = {};
DuAEExpression.Library["getLayerCompPos"].expression = ['function getLayerCompPos( t, l ) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof l === \'undefined\') l = thisLayer;',
	'if (l.hasParent) return l.parent.toComp(l.position, t);',
	'return l.toComp(l.anchorPoint, t);',
	'}'
	].join('\n');
DuAEExpression.Library["getLayerCompPos"].requirements = [];

/**
 * Gets the world position of a layer.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {Layer} other The other layer
 * @param {Layer} [origin=thisLayer] The origin
 * @param {number} [t=time] Time from when to get the position
 * @return {number[]} The world position
 * @requires getLayerWorldPos
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getLayerDistance"] = {};
DuAEExpression.Library["getLayerDistance"].expression = ['function getLayerDistance(other, origin, t) {',
	'if (typeof origin === \'undefined\') origin = thisLayer;',
	'if (typeof t === \'undefined\') t = time;',
	'var p1 = getLayerWorldPos(t, other);',
	'var p2 = getLayerWorldPos(t, origin);',
	'return length(p1, p2);',
	'}'
	].join('\n');
DuAEExpression.Library["getLayerDistance"].requirements = ["getLayerWorldPos"];

/**
 * Gets the world position of a layer.
 * @function
 * @param {number} [t=time] Time from when to get the position
 * @param {Layer} [l=thisLayer] The layer
 * @return {number[]} The world position
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getLayerWorldPos"] = {};
DuAEExpression.Library["getLayerWorldPos"].expression = ['function getLayerWorldPos(t, l) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof l === \'undefined\') l = thisLayer;',
	'if (l.hasParent) return l.parent.toWorld(l.position, t);',
	'return l.position.valueAtTime(t);',
	'}'
	].join('\n');
DuAEExpression.Library["getLayerWorldPos"].requirements = [];

/**
 * Gets the world instant speed of a layer.
 * @function
 * @param {number} [t=time] The time when to get the velocity
 * @param {Layer} [l=thisLayer] The layer
 * @return {number} A positive number. The speed.
 * @requires getLayerWorldVelocity
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getLayerWorldSpeed"] = {};
DuAEExpression.Library["getLayerWorldSpeed"].expression = ['function getLayerWorldSpeed(t, l) {',
	'return length(getWorldVelocity(t, l));',
	'}'
	].join('\n');
DuAEExpression.Library["getLayerWorldSpeed"].requirements = ["getLayerWorldVelocity"];

/**
 * Gets the world instant velocity of a layer.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {number} [t=time] The time when to get the velocity
 * @param {Layer} [l=thisLayer] The layer
 * @return {number[]} The velocity.
 * @requires getLayerWorldPos
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getLayerWorldVelocity"] = {};
DuAEExpression.Library["getLayerWorldVelocity"].expression = ['function getLayerWorldVelocity(t, l) {',
	'return (getLayerWorldPos(t, l) - getLayerWorldPos(t - 0.01, l)) * 100;',
	'}'
	].join('\n');
DuAEExpression.Library["getLayerWorldVelocity"].requirements = ["getLayerWorldPos"];

/**
 * Gets the world orientation of a (2D) layer.
 * @function
 * @param {Layer} l The layer to get the orientation from
 * @return {float} The orientation, in degrees.
 * @requires sign
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getOrientation"] = {};
DuAEExpression.Library["getOrientation"].expression = ['function getOrientation( l ) {',
	'var sign = getScaleMirror( l );',
	'var uTurn = getScaleUTurn( l )',
	'var r = l.rotation.value * sign + uTurn;',
	'while ( l.hasParent ) {',
	'l = l.parent;',
	'var lr = l.rotation.value;',
	'if (l.hasParent) {',
	'var s = l.parent.scale.value;',
	'lr *= Math.sign(s[0]*s[1]);',
	'}',
	'r += lr;',
	'}',
	'return r;',
	'}',
	'function getScaleMirror( l ) {',
	'var sign = 1;',
	'while (l.hasParent) {',
	'l = l.parent;',
	'var s = l.scale.value;',
	'sign *= Math.sign(s[0]*s[1]);',
	'}',
	'return sign;',
	'}',
	'function getScaleUTurn( l ) {',
	'var u = 1;',
	'while (l.hasParent) {',
	'l = l.parent;',
	'var s = l.scale.value;',
	'u = u*s[1];',
	'}',
	'if (u < 0) return 180;',
	'else return 0;',
	'}'
	].join('\n');
DuAEExpression.Library["getOrientation"].requirements = ["sign"];

/**
 * Gets the world orientation of a (2D) layer at a specific time.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {Layer} l The layer to get the orientation from
 * @param {float} [t=time] The time at which to get the orientation
 * @return {float} The orientation, in degrees.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getOrientationAtTime"] = {};
DuAEExpression.Library["getOrientationAtTime"].expression = ['function getOrientationAtTime( l, t ) {',
	'var r = 0;',
	'r += l.rotation.valueAtTime( t );',
	'while ( l.hasParent ) {',
	'l = l.parent;',
	'r += l.rotation.valueAtTime( t );',
	'}',
	'return r;',
	'}'
	].join('\n');
DuAEExpression.Library["getOrientationAtTime"].requirements = [];

/**
 * Gets the world speed of a property.
 * @function
 * @param {number} [t=time] Time from when to get the position
 * @param {Layer} [prop=thisProperty] The property
 * @return {number[]} The world speed
 * @requires getPropWorldVelocity
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPropWorldSpeed"] = {};
DuAEExpression.Library["getPropWorldSpeed"].expression = ['function getPropWorldSpeed(t, prop) {',
	'return length(getPropWorldVelocity(t, prop));',
	'}'
	].join('\n');
DuAEExpression.Library["getPropWorldSpeed"].requirements = ["getPropWorldVelocity"];

/**
 * Gets the world coordinates of a property.
 * @function
 * @param {number} [t=time] Time from when to get the position
 * @param {Layer} [prop=thisProperty] The property
 * @return {number[]} The world coordinates
 * @requires getLayerWorldPos
 * @requires isPosition
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPropWorldValue"] = {};
DuAEExpression.Library["getPropWorldValue"].expression = ['function getPropWorldValue(t, prop) {',
	'if (typeof t === \'undefined\') t = time;',
	'if (typeof prop === \'undefined\') prop = thisProperty;',
	'if (isPosition(prop)) return getLayerWorldPos(t, thisLayer);',
	'return thisLayer.toWorld(prop.valueAtTime(t), t);',
	'}'
	].join('\n');
DuAEExpression.Library["getPropWorldValue"].requirements = ["getLayerWorldPos","isPosition"];

/**
 * Gets the world velocity of a property.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {number} [t=time] Time from when to get the position
 * @param {Layer} [prop=thisProperty] The property
 * @return {number[]} The world velocity
 * @requires getPropWorldValue
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getPropWorldVelocity"] = {};
DuAEExpression.Library["getPropWorldVelocity"].expression = ['function getPropWorldVelocity(t, prop) {',
	'return (getPropWorldValue(t + 0.005, prop) - getPropWorldValue(t - 0.005, prop)) * 100;',
	'}'
	].join('\n');
DuAEExpression.Library["getPropWorldVelocity"].requirements = ["getPropWorldValue"];

/**
 * Gets the world scale of a layer.
 * @function
 * @param {Layer} l The layer to get the scale from
 * @return {float[]} The scale, in percent.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["getScale"] = {};
DuAEExpression.Library["getScale"].expression = ['function getScale( l ) {',
	'var s = l.scale.value;',
	'var threeD = s.length == 3;',
	'while ( l.hasParent ) {',
	'l = l.parent;',
	'var ps = l.scale.value / 100;',
	'if (threeD && ps.length == 3) {',
	's = [ s[0]*ps[0], s[1]*ps[1], s[2]*ps[2] ];',
	'}',
	'else if (threeD) {',
	's = [ s[0]*ps[0], s[1]*ps[1], s[2] ];',
	'}',
	'else {',
	's = [ s[0]*ps[0], s[1]*ps[1] ];',
	'}',
	'}',
	'return s;',
	'}'
	].join('\n');
DuAEExpression.Library["getScale"].requirements = [];

/**
  * 2D transformation matrix object initialized with identity matrix. See the source code for more documentation.
  * @class
  * @classdesc 2D Transformation Matrix. Simplified for use in After Effects Expressions by Nicolas "Duduf" Dufresne
  * @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 ExpressionLibrary
*/
DuAEExpression.Library["Matrix"] = {};
DuAEExpression.Library["Matrix"].expression = ['function Matrix() {',
	'/*!',
	'2D Transformation Matrix v2.7.5 LT',
	'(c) Epistemex.com 2014-2018',
	'License: MIT',
	'*/',
	'var me = this, _el;',
	'me._t = me.transform;',
	'me.a = me.d = 1;',
	'me.b = me.c = me.e = me.f = 0;',
	'}',
	'Matrix.prototype = {',
	'rotate: function(angle) {',
	'angle = degreesToRadians(angle);',
	'var',
	'cos = Math.cos(angle),',
	'sin = Math.sin(angle);',
	'return this._t(cos, sin, -sin, cos, 0, 0)',
	'},',
	'rotateFromVector: function(x, y) {',
	'return this.rotate(typeof x === "number" ? Math.atan2(y, x) : Math.atan2(x.y, x.x))',
	'},',
	'scale: function(s) {',
	'return this._t(s[0], 0, 0, s[1], 0, 0);',
	'},',
	'shear: function(sx, sy) {',
	'return this._t(1, sy, sx, 1, 0, 0)',
	'},',
	'skew: function(ax, ay) {',
	'return this.shear(Math.tan(ax), Math.tan(ay))',
	'},',
	'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: function(t) {',
	'return this._t(1, 0, 0, 1, t[0], t[1]);',
	'},',
	'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()',
	'},',
	'multiply: function(m) {',
	'return this._t(m.a, m.b, m.c, m.d, m.e, m.f)',
	'},',
	'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: 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,',
	'r, s;',
	'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 {',
	'scale = {x: 0, y: 0};',
	'}',
	'return {',
	'translate: translate,',
	'rotation : rotation,',
	'scale	: scale,',
	'skew	 : skew',
	'}',
	'},',
	'determinant: function() {',
	'return this.a * this.d - this.b * this.c',
	'},',
	'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;',
	'},',
	'isIdentity: function() {',
	'var me = this;',
	'return me.a === 1 && !me.b && !me.c && me.d === 1 && !me.e && !me.f',
	'},',
	'isInvertible: function() {',
	'return !this._q(this.determinant(), 0)',
	'},',
	'isValid: function() {',
	'return !(this.a * this.d)',
	'},',
	'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)',
	'},',
	'clone: function(noContext) {',
	'return new Matrix(noContext ? null : this.context).multiply(this)',
	'},',
	'_q: function(f1, f2) {',
	'return Math.abs(f1 - f2) < 1e-14',
	'},',
	'_x: function() {',
	'var me = this;',
	'try { if (me.context)',
	'me.context.setTransform(me.a, me.b, me.c, me.d, me.e, me.f);',
	'} catch(e) {}',
	'return me',
	'}',
	'};'
	].join('\n');
DuAEExpression.Library["Matrix"].requirements = [];

/**
 * Transform the points from layer to world coordinates
 * @function
 * @param {float[][]} points The points
 * @param {Layer} layer The layer
 * @return {float[][]} The points in world coordinates
 * @category ExpressionLibrary
 */
DuAEExpression.Library["pointsToWorld"] = {};
DuAEExpression.Library["pointsToWorld"].expression = ['function pointsToWorld( points, layer ) {',
	'for (var i = 0; i < points.length; i++) {',
	'points[i] = layer.toWorld(points[i]);',
	'}',
	'return points;',
	'}'
	].join('\n');
DuAEExpression.Library["pointsToWorld"].requirements = [];

/**
 * Gets the points of the shape path in layer coordinates (applies the group transform)
 * @function
 * @param {Property} prop The property from which to get the path
 * @return {float[][]} The points in layer coordinates
 * @requires getGroupTransformMatrix
 * @category ExpressionLibrary
 */
DuAEExpression.Library["shapePointsToLayer"] = {};
DuAEExpression.Library["shapePointsToLayer"].expression = ['function shapePointsToLayer( prop ) {',
	'var points = prop.points();',
	'var matrix = getGroupTransformMatrix( prop );',
	'for (var i = 0; i < points.length; i++) {',
	'points[i] = matrix.applyToPoint( points[i] );',
	'}',
	'return points;',
	'}'
	].join('\n');
DuAEExpression.Library["shapePointsToLayer"].requirements = ["getGroupTransformMatrix"];

/**
 * Translates a point with a layer, as if it was parented to it.<br />
 * Note that for performance reasons with expressions, even if the parameters of the function are documented with optional/default values, you MUST provide ALL the arguments when using them.
 * @function
 * @param {Layer} l The layer to get the translation from.
 * @param {float[]} [point=[0,0]] The [X,Y] point to translate (using world coordinates).
 * @param {float} [startT=0] The start time of the translation
 * @param {float} [endT=time] The end time of the translation
 * @return {float[]} The coordinates of the translated point.
 * @category ExpressionLibrary
 */
DuAEExpression.Library["translatePointWithLayer"] = {};
DuAEExpression.Library["translatePointWithLayer"].expression = ['function translatePointWithLayer( l, point, startT, endT ) {',
	'try {',
	'var pos = l.fromWorld( point, startT );',
	'} catch ( e ) {',
	'var pos = [ 0, 0 ];',
	'}',
	'var prevPos = l.toWorld( pos, startT );',
	'var newPos = l.toWorld( pos, endT );',
	'return newPos - prevPos;',
	'}'
	].join('\n');
DuAEExpression.Library["translatePointWithLayer"].requirements = [];

// ==================== |---------| ====================
// ==================== | project | ====================
// ==================== |---------| ====================

/**
 * After Effects project methods
 * @namespace
 * @category DuAEF
 */
var DuAEProject = {};

//undocumented flags
DuAEProject.progressMode = false;
DuAEProject.activeComp = null;

/**
 * Sets the project in "waiting mode" to speed up things.
 * @param {Boolean} [inProgress=true] - True to set progress mode, false to stop it.
 * @param {Boolean} [showProgressBar=true] - Will show a nice progress bar if true
 */
DuAEProject.setProgressMode = function(inProgress, showProgressBar) {
    inProgress = def(inProgress, true);
    showProgressBar = def(showProgressBar, true);

    if (showProgressBar && inProgress) {
        DuScriptUI.progressBar.reset();
        DuScriptUI.progressBar.show();
    } else if (showProgressBar) DuScriptUI.progressBar.hide();

    if (!inProgress && !DuAEProject.progressMode) return;

    if (inProgress) {

        //if not already in progress, create temp comp etc
        if (!DuAEProject.progressMode) {
            // Hide layer controls
            // not anymore: the doc has to explain it's better to hide them
            // but to do it automatically we need a way to get the info to not
            // enable them if they're already hidden
            //DuAE.toggleLayerControls();

            //store the current comp
            DuAEProject.activeComp = DuAEProject.getActiveComp();
        }

        DuAEProject.progressMode = true;
    } else {
        // Show layer controls
        //DuAE.toggleLayerControls();

        DuAEProject.progressMode = false;
    }
}

/**
 * Gets all compositions in the project (or only the root of the project, ignoring subfolders)
 * @param {boolean} [rootOnly=false] Set to true to get only comps from the root of the project
 * @return {CompItem[]} The compositions
 */
DuAEProject.getComps = function(rootOnly) {
    rootOnly = def(rootOnly, false);
    var comps = [];
    var it = new DuList(app.project.items);
    it.do(function(comp) {
        if (comp instanceof CompItem) {
            if (rootOnly && comp.parentFolder != app.project.rootFolder) return;
            comps.push(comp);
        }
    });
    return comps;
}

/**
 * Gets all selected compositions in the project
 * @return {CompItem[]} The compositions
 */
DuAEProject.getSelectedComps = function() {
    var selectedItems = app.project.selection;
    var comps = [];
    for (var i = 0, n = selectedItems.length; i < n; i++) {
        var item = selectedItems[i];
        if (item instanceof CompItem) comps.push(item);
    }
    return comps;
}

/**
 * Gets either the active comp or the first selected one
 * @return {CompItem|null} The composition
 */
DuAEProject.getSelectedComp = function() {
    var comp = DuAEProject.getActiveComp();
    if (!comp) {
        var items = app.project.selection;
        for(var i = 0; i < items.length; i++)
        {
            var c = items[i];
            if (c instanceof CompItem) {
                comp = c;
                break;
            }
        }
    }
    return comp;
}

/**
 * Runs a function on all comps of the project
 * @param {function} func The function, which must take a CompItem as its single argument.
 * @param {Boolean} [selectedOnly=false] Runs only on selected compositions
 */
DuAEProject.doComps = function(func) {
    // Get items
    var it = new DuList(app.project.items);
    it.do(function(item) {
        if (item instanceof CompItem) func(item);
    });
}

/**
 * Gets the After Effects current composition
 * @return {CompItem|null} The current composition or null if there's no current comp
 */
DuAEProject.getActiveComp = function() {
    if (DuAEProject.progressMode) return DuAEProject.activeComp;
    //activate the viewer first to try to get the opened composition
    if (app.activeViewer != null) {
        if (app.activeViewer.type == ViewerType.VIEWER_COMPOSITION) app.activeViewer.setActive();
    }
    var comp = app.project.activeItem;
    if (!comp) return null;
    if (!(comp instanceof CompItem)) return null;
    return comp;
}

/**
 * Retrieves an item by its Item ID
 * @return {Item|null} The item
 */
DuAEProject.getItemById = function(id) {

    if (DuAE.isVersionAtLeast('CC2014')) return app.project.itemByID(id);

    var it = new DuList(app.project.items);
    while (item = it.next()) {
        if (item.id == id) return item;
    }
    return null;
}

/**
 * Retrieves the first item with the given name
 * @return {Item|null} The item
 */
DuAEProject.getItemByName = function(name) {
    var it = new DuList(app.project.items);
    while (item = it.next()) {
        if (item.name == name) return item;
    }
    return null;
}

/**
 * Generates a new unique name for a composition
 * @param {string} newName - The wanted new name
 * @param {CompItem} comp  - The comp
 * @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.
 */
DuAEProject.newUniqueCompName = function(newName, increment) {
    increment = def(increment, true);
    var compNames = [];
    var it = new DuList(app.project.items);
    it.do(function(comp) {
        if (comp instanceof CompItem) compNames.push(comp.name);
    });
    return DuString.generateUnique(newName, compNames, increment);
}

/**
 * Checks if all comps have a different name.
 * @return {Object} The list of names used several times. Check the length attribute to know how many duplicates were found, loop through the keys to get the names. Eech key is an array containing the list of comps with that name.
 * @example
 * var dupes = DuAEProject.checkCompNames();
 * if (dupes.length != 0) {
 * for (name in dupes)
 * {
 *     if (dupes.hasOwnProperty(name)) alert(dupes[name]); //dupes[name] is an array of Layer
 * }
 * } */
DuAEProject.checkCompNames = function() {
    var duplicatedNames = {};
    duplicatedNames.length = 0;
    var compNames = {};
    var items = app.project.items;
    for (var i = 1, n = items.length; i <= n; i++) {
        var comp = items[i];
        if (comp instanceof CompItem) {
            var name = comp.name;
            if (duplicatedNames.hasOwnProperty(name)) {
                duplicatedNames[name].push(comp);
                continue;
            }
            if (compNames.hasOwnProperty(name)) {
                duplicatedNames[name] = [compNames[name], comp];
                duplicatedNames.length++;
                continue;
            }
            compNames[name] = comp;
        }
    }
    return duplicatedNames;
}

/**
 * Gets the size of the current project file
 * @return {int} the size in Bytes. -1 if it has not been saved yet.
 */
DuAEProject.getSize = function() {
    var file = app.project.file;
    if (!(file instanceof File)) return -1;
    if (!file.exists) return -1;
    else return file.length;
}

/**
 * The Expressions Engine setting in the Project Settings dialog box, as a string.
 * @return {string} One of: "extendscript", "javascript-1.0"
 */
DuAEProject.expressionEngine = function() {
    var engine = app.project.expressionEngine;
    if (!engine) engine = "extendscript";
    return engine;
}

/**
 * Makes sure all compositions in the project have unique names, renaming them if needed.
 * @param {Array|ItemCollection} [comps] A list of comps, all of them by default
 */
DuAEProject.setUniqueCompNames = function(comps) {
    comps = def(comps, DuAEProject.getComps());
    var it = new DuList(comps);
    it.do(function(comp) {
        //temporarily set another name to correctly generate a new unique name 
        var oldName = comp.name;
        comp.name = '***Duik-temp-name-xxx***';
        comp.name = DuAEProject.newUniqueCompName(oldName);
        app.project.autoFixExpressions(oldName, comp.name);
    });
}

/**
 * Reduces the project using the selected compositions, the same way the native command does it, but being able to keep comps used only by expressions. If there's no comp selected, will use all comps at the root of the project.
 * @param {boolean} [keepExpressionOnly=true] Set to false to ignore comps used only by expressions (same as the native command)
 */
DuAEProject.reduceSelected = function(keepExpressionOnly) {
    var comps = [];
    for (var i = 0, num = app.project.selection.length; i < num; i++) {
        var item = app.project.selection[i];
        if (item instanceof CompItem) comps.push(item);
    }
    DuAEProject.reduce(comps, keepExpressionOnly);
}

/**
 * Reduces the project, the same way the native command does it, but being able to keep comps used only by expressions.
 * @param {CompItem|CompItem[]} [comps] The name or the id of the comp(s) to keep. If omitted, it will use all comps at the root of the project (the ones not in a subfolder).
 * @param {boolean} [keepExpressionOnly=true] Set to false to ignore comps used only by expressions (same as the native command)
 */
DuAEProject.reduce = function(comps, keepExpressionOnly) {
    keepExpressionOnly = def(keepExpressionOnly, true);
    comps = def(comps, DuAEProject.getComps(true));
    if (comps instanceof CompItem) comps = [comps];
    if (comps.length == 0) return;
    comps = new Dulist(comps);

    //consolidate footage
    app.project.consolidateFootage();

    //update cache before running
    DuAEProject.updateExpressionCache();

    for (i = app.project.numItems; i > 0; i--) {
        var item = app.project.item(i);
        //if it's one of the comp, ignore
        if (comps.indexOf(item, DuAEItem.compareItems) >= 0) continue;
        //if AVItem
        if (item instanceof CompItem || item instanceof FootageItem) {
            var usedIn = DuAEItem.usedIn(item, true, keepExpressionOnly);

            if (usedIn.length == 0) {
                item.remove();
                continue;
            }

            var remove = true;

            for (j = 0, num = usedIn.length; j < num; j++) {
                //if there's one of the comp to keep, do not remove
                if (comps.indexOf(usedIn[j], DuAEItem.compareItems) >= 0) {
                    remove = false;
                    break;
                }
            }

            if (remove) item.remove();
        }
        //remove folder if  empty 
        else if (item.numItems == 0) item.remove();
    }
}

/**
 * Collects all dependencies in a folder
 * @param {Folder} [destination] The folder where to save the files. The project files will be collected in a subfolder called "project name.aep.archive" or "project name.aep.zip". If not provided, will use the current folder.
 * @param {boolean} [overwrite=false] Whether to overwrite existing footage in the destination.
 * @param {boolean} [zip=false] Set to true to automatically zip the archive.
 * @param {boolean} [createProjectFolder=true] Whether to create a folder for this project or use the destination as is.
 */
DuAEProject.collectFiles = function(destination, overwrite, zip, createProjectFolder) {
    var projectFile = app.project.file;

    var result = {};
    result.missingFootages = [];

    destination = def(destination, projectFile.parent);
    overwrite = def(overwrite, false);
    zip = def(zip, false);
    createProjectFolder = def(createProjectFolder, true);

    //create folder for the project
    var projectName = DuPath.getBasename(projectFile.name);
    var projectFolderPath = destination.absoluteURI;
    if (createProjectFolder) {
        projectFolderPath += '/' + projectName + ".archive";
    }
    var projectFolder = new Folder(projectFolderPath);
    var footageFolder = new Folder(projectFolder.absoluteURI + "/(Footage)/");
    projectFolder.create();
    footageFolder.create();

    //create report file
    var missingFootageString = "=== MISSING FOOTAGE ===\n\n";

    var currentFootageFolder = footageFolder;
    currentFootageFolder.id = -1;

    //for each item, copy and re-link
    for (var i = 1, num = app.project.numItems; i <= num; i++) {
        var item = app.project.item(i);

        //check if we're still in the right folder
        if (item.parentFolder.id != currentFootageFolder.id) {
            if (item.parentFolder == app.project.rootFolder) {
                currentFootageFolder = footageFolder;
            } else {
                currentFootageFolder = currentFootageFolder.parent;
            }
            currentFootageFolder.id = item.parentFolder.id;
        }

        if (item instanceof CompItem) continue;
        if (item instanceof FolderItem) {
            currentFootageFolder = new Folder(currentFootageFolder.absoluteURI + "/" + item.name);
            currentFootageFolder.create();
            currentFootageFolder.id = item.id;
            continue;
        }

        //if no source file
        if (item.file == null) continue;
        //if missing source
        if (item.footageMissing) {
            missingFootageString += item.name + " @ " + item.file.fsName + "\n";
            result.missingFootages.push(item.file.fsName);
            continue;
        }

        //if this is not an image sequence
        if (!DuAEItem.isImageSequence(item)) {
            var newPath = currentFootageFolder.absoluteURI + "/" + item.file.name;
            var newFile = new File(newPath);
            //copy
            if ((newFile.exists && overwrite) || !newFile.exists) item.file.copy(newPath);
            //re-link
            item.replace(new File(newPath));
        } else {
            //get files and create a folder
            var sequenceFolder = item.file.parent;
            var frameSequence = sequenceFolder.getFiles();
            var sequenceFolderTarget = new Folder(currentFootageFolder.absoluteURI + "/" + sequenceFolder.name + "/");
            sequenceFolderTarget.create();
            var newPath = sequenceFolderTarget.absoluteURI + "/" + item.file.name;
            var newFile = new File(newPath);

            //copy frames
            if ((newFile.exists && overwrite) || !newFile.exists) {
                for (var j = 0, numFrames = frameSequence.length; j < numFrames; j++) {
                    var frame = frameSequence[j];
                    if (frame instanceof Folder) continue;
                    frame.copy(sequenceFolderTarget.absoluteURI + "/" + frame.name);
                }
            }

            //re-link
            item.replaceWithSequence(newFile, true);
        }

    }

    //sauvegarder le projet
    var newProjectFileName = DuPath.newUniqueName(app.project.file.name, projectFolder);
    app.project.save(new File(projectFolder.absoluteURI + "/" + newProjectFileName));

    //write report
    var reportFile = new File(projectFolder.absoluteURI + "/" + app.project.file.name + "_report.txt");
    var report = app.project.file.name + "\n\n";
    report += missingFootageString;
    DuFile.write(reportFile, report);

    //ZIP
    if (zip) {
        app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
        app.open(projectFile);
        var zipFile = DuZip.compressFolderContent(projectFolder, projectName + ".zip", true);
    }
}

/**
 * Replaces text in Expressions
 * @param {string} oldString - The string to replace
 * @param {string} newString - The new string
 * @param {boolean} [caseSensitive=true] - Whether the search has to be case sensitive
 */
DuAEProject.replaceInExpressions = function(oldString, newString, caseSensitive) {
    caseSensitive = def(caseSensitive, true);

    var re = new RegExp(DuRegExp.escape(oldString), caseSensitive ? 'g' : 'gi');

    DuAEExpression.doInExpresssions(function(e) {
        e.changed = re.test(e.expression);
        if (e.changed) e.expression = e.expression.replace(re, newString);
        return e;
    });
}

/**
 * Gets the unused footages.
 * @return {FootageItem[]} The list of unused items.
 */
DuAEProject.getUnusedFootages = function() {
    var unused = [];
    for (var i = 1, n = app.project.numItems; i <= n; i++) {
        var item = app.project.item(i);
        if (item instanceof FootageItem) {
            if (item.usedIn.length == 0) unused.push(item);
        }
    }
    return unused;
}

/**
 * Gets a folder with its name. If name is "Project Root" or empty, returns the root of the project.
 * @param {string} folderName The name of the folder
 * @return {FolderItem|null} The folder or null if not found.
 */
DuAEProject.getFolderItem = function(folderName) {
    folderName = def(folderName, "");
    if (folderName == "") return app.project.rootFolder;
    if (DuString.trim(folderName) == "") return app.project.rootFolder;
    if (folderName.toLowerCase() == i18n._("Project Root").toLowerCase()) return app.project.rootFolder;

    for (var i = 1, n = app.project.numItems; i <= n; i++) {
        var item = app.project.item(i);
        if (item instanceof FolderItem) {
            if (item.name == folderName) return item;
        }
    }

    return null;
}

/**
 * Gets the unused compositions, except the ones in the given folder.
 * @param {FolderItem} [folder] The folder to exclude.
 * @return {CompItem[]} The unused compositions
 */
DuAEProject.getUnusedComps = function(folder) {
    var comps = []
    folder = def(folder, null);
    for (var i = 1, n = app.project.numItems; i <= n; i++) {
        var item = app.project.item(i);
        if (item instanceof CompItem) {
            if (item.usedIn.length == 0) {
                if (folder && folder.id == app.project.rootFolder.id && item.parentFolder == folder.id) continue;
                if (folder && DuAEItem.isInFolder(item, folder)) continue;
                comps.push(item);
            }
        }
    }
    return comps;
}

/**
 * Gets all the precompositions located at the root of the project.
 * @return {CompItem[]}
 */
DuAEProject.getPrecompsAtRoot = function() {
    var precomps = [];
    for (var i = 1, n = app.project.numItems; i <= n; i++) {
        var item = app.project.item(i);
        if (item instanceof CompItem) {
            if (item.parentFolder.id == app.project.rootFolder.id && item.usedIn.length != 0) precomps.push(item);
        }
    }
    return precomps;
}

/**
 * Gets the project name (i.e. the file name without extension)
 * @return {string} The project name.
 */
DuAEProject.name = function() {
    return DuPath.getBasename(app.project.file);
}

/**
 * Bakes the expressions to keyframes
 * @param {DuAEExpression.BakeAlgorithm} [mode=DuAEExpression.BakeAlgorithm.SMART] The algorithm to use for baking the expressions.
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 */
DuAEProject.bakeExpressions = function(mode, frameStep) {
    DuAEProject.doComps(function(comp) {
        DuAEComp.bakeExpressions(mode, frameStep, comp);
    });
}

/**
 * Bakes the expressions to keyframes
 * @param {DuAEExpression.BakeAlgorithm} [mode=DuAEExpression.BakeAlgorithm.SMART] The algorithm to use for baking the expressions.
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 */
DuAEProject.bakeCompositions = function(mode, frameStep) {
    DuAEProject.doComps(function(comp) {
        DuAEComp.bake(mode, frameStep, comp);
    });
}// ==================== |------------| ====================
// ==================== | projectXMP | ====================
// ==================== |------------| ====================

/**
 * Manages XMP Metadata of the project.
 * @namespace
 * @category DuAEF
 */
var DuAEProjectXMP = {};

/**
 * Gets the XMP of the current project
 * @return {XMPMeta} The XMP data
 */
DuAEProjectXMP.getXmp = function()
{
    DuXMP.init();
    if (!app.project.file) return null;
    return new XMPMeta(app.project.xmpPacket)
}

/**
 * Gets the value of a property.
 * @param {string} prop The name of the property
 * @param {XMPConst} [type] The property data type, one of: - XMPConst.STRING - XMPConst.INTEGER - XMPConst.NUMBER - XMPConst.BOOLEAN - XMPConst.XMPDATE
 * @return {Object} The value
 */
DuAEProjectXMP.getPropertyValue = function( prop, type)
{
    var xmp = DuAEProjectXMP.getXmp();
    if (!xmp) return null;

    var p = xmp.getProperty(XMPConst.NS_XMP, prop);

    if (!p) return null;
    return p.value;
}

/**
 * Sets the value of a property.
 * @param {string} prop The name of the property
 * @param {Object} value The value
 */
DuAEProjectXMP.setPropertyValue = function(prop, value)
{
    var xmp = DuAEProjectXMP.getXmp();
    if (!xmp) return null;

    var type;
    var options = 0;
    var isArray = false;

    //Check type
    //TODO if array, use setArrayItem (see XMP doc)
    if (jstype(value) === 'array')
    {
        options = XMPConst.PROP_IS_ARRAY;
        isArray = true;
    }

    var testVal;
    if (isArray && value.length > 0) testVal = value[0];
    else testVal = value;

    if (jstype(testVal) === 'string')
    {
        type = XMPConst.STRING;
    }
    else if (jstype(testVal) === 'number')
    {
        var testInt = Math.round(testVal);
        if (testInt == testVal) type = XMPConst.INTEGER;
        else type = XMPConst.NUMBER;
    }
    else if (jstype(testVal) === 'boolean')
    {
        type = XMPConst.BOOLEAN;
    }
    else if (jstype(testVal) === 'date')
    {
        type = XMPConst.XMPDATE;
    }

    xmp.setProperty(XMPConst.NS_XMP, prop, value, options, type);
    app.project.xmpPacket = xmp.serialize();
}

/**
 * Gets the project settings stored in XMP by DuAEF.
 * @return {object} The settings.
 */
DuAEProjectXMP.getSettings = function ()
{
    var json = JSON.parse(DuAEProjectXMP.getPropertyValue("DuAEF"));
    if (!json) json = {};
    return json;
}

/**
 * Saves the project settings stored in XMP by DuAEF.
 * @param {object} settings The settings.
 */
DuAEProjectXMP.saveSettings = function (settings)
{
    var str = JSON.stringify(settings);
    DuAEProjectXMP.setPropertyValue("DuAEF", str);
}

/**
 * The Settings of the current project, an object similar to {@link DuSettings} except that it saves in the Project XMP
 */
DuAEProject.settings = {}

/**
 * The settings data
 */
DuAEProject.settings.data = {};

/**
 * Updates the settings: reads them in the XMP of the current project. Call this function before reading the data.
 */
DuAEProject.settings.update = function()
{
    DuAEProject.settings.data = DuAEProjectXMP.getSettings();
}

/**
 * Saves the settings to the project XMP
 */
DuAEProject.settings.save = function()
{
    DuAEProjectXMP.saveSettings(DuAEProject.settings.data);
}// ==================== |------| ====================
// ==================== | item | ====================
// ==================== |------| ====================

/**
 * After Effects item methods
 * @namespace
 * @category DuAEF
 */
var DuAEItem = {};

DuAEItem.stillFormats = [
    "bmp",
    "rle",
    "dib",
    "tif",
    "crw",
    "nef",
    "raf",
    "orf",
    "mrw",
    "dcr",
    "mos",
    "raw",
    "pef",
    "srf",
    "dng",
    "x3f",
    "cr2",
    "erf",
    "sr2",
    "kdc",
    "mfw",
    "mef",
    "arw",
    "nrw",
    "rw2",
    "rwl",
    "iiq",
    "3fr",
    "fff",
    "srw",
    "gpr",
    "dxo",
    "heic",
    "arq",
    "cr3",
    "cin",
    "dpx",
    "img",
    "ei",
    "ai",
    "pdf",
    "eps",
    "ai3",
    "ai4",
    "ai5",
    "ai6",
    "ai7",
    "ai8",
    "epsf",
    "epsp",
    "jpg",
    "jpeg",
    "jfif",
    "jpe",
    "ico",
    "exr",
    "sxr",
    "mxr",
    "psd",
    "psb",
    "png",
    "hdr",
    "rgbe",
    "xyze",
    "rla",
    "rpf",
    "sgi",
    "bw",
    "rgb",
    "pic",
    "tga",
    "vda",
    "icb",
    "vst",
    "tiff"
]

//low-level undocumented method
//a function to compare items
DuAEItem.compareItems = function (itemA, itemB) { return itemA.id == itemB.id; }

/**
 * Gets the compositions this item is used in.
 * @param {AVItem} item The item
 * @param {boolean} [includeExpressionOnly=false] Check for comps using this item only through expressions. The cache has to be updated with {@link DuAEProject.updateExpressionCache} before using this method with this argument set to true.
 * @param {boolean} [recursive=false] Check recursively in parent comps too.
 * @return {CompItem[]} The compositions.
 */
DuAEItem.usedIn = function (item, recursive, keepExpressionOnly ) {
    keepExpressionOnly = def (keepExpressionOnly, false);
    recursive = def (recursive, false);
    var cacheSuspended = false;

    if (item instanceof FolderItem) return [];

    var usedIn = item.usedIn;

    if (!keepExpressionOnly) return usedIn;

    //update cache if needed
    var currentTime = new Date().getTime();
    if ( ( currentTime - DuAEProject.lastExpressionCacheUpdateTime > 60000 ) || DuAEProject.expressionCache.length == 0) {
        DuAEProject.updateExpressionCache();
        DuAEProject.suspendCacheUpdates = true;
        cacheSuspended = true;
    }

    //the regexps we need 
    var regExp = '';
    var itemNameRegEx = DuRegExp.escape(item.name);
    if (item instanceof CompItem ) regExp = new RegExp("comp\\s*\\(\\s*(\"|')" + itemNameRegEx + "\\1\\s*\\)");
    else regExp = new RegExp("footage\\s*\\(\\s*(\"|')" + itemNameRegEx + "\\1\\s*\\)");

    //check for expressions using this item
    for (var i = 0, num = DuAEProject.expressionCache.length; i < num; i++)
    {
        var exp = DuAEProject.expressionCache[i];
        if ( regExp.test(exp.expression) ) usedIn.push( exp.layer.containingComp );
    }

    if ( recursive ) {
        for ( var i = 0, num = usedIn.length; i < num; i++) {
            usedIn = usedIn.concat( DuAEItem.usedIn( usedIn[i] , true, keepExpressionOnly ) );
        }
    }

    usedIn = new DuList(usedIn);
    usedIn.removeDuplicates(DuAEItem.compareItems);

    if (cacheSuspended) DuAEProject.suspendCacheUpdates = false;

    return usedIn;
}

/**
 * Checks if this item source is an image sequence
 * @param {FootageItem} item The item
 * @return {boolean} true if the source is an image sequence
 */
DuAEItem.isImageSequence = function (item) {
    if (!(item instanceof FootageItem)) return false;

    //check if it's still
    if (item.mainSource.isStill) return false;

    //check if it's a video 
    var extension = DuPath.getExtension(item.file);
    extension = extension.toLowerCase();

    stillfmts = new DuList( DuAEItem.stillFormats )

    return stillfmts.indexOf( extension ) >= 0;

}

/**
 * Checks recursively (except for root folder) if an item is in a specific folder
 * @param {Item} item The item to check
 * @param {FolderItem} folder The folder
 * @return {boolean}
 */
DuAEItem.isInFolder = function (item, folder)
{
    var pFolder = item.parentFolder;
    if (pFolder.id == app.project.rootFolder.id && pFolder.id == folder.id) return true;
    while (pFolder && pFolder.id != app.project.rootFolder.id)
    {
        if (pFolder.id == folder.id) return true;
        pFolder = pFolder.parentFolder;
    }
    return false;
}// ==================== |------| ====================
// ==================== | comp | ====================
// ==================== |------| ====================

/**
 * After Effects composition methods
 * @namespace
 * @category DuAEF
 */
var DuAEComp = {};

/**
 * Associative array to get Comp Renderer names from their matchNames
 */
DuAEComp.RendererNames = {};
DuAEComp.RendererNames['ADBE Advanced 3d'] = "Classic 3D";
DuAEComp.RendererNames['ADBE Standard 3d'] = "???";
DuAEComp.RendererNames['ADBE Picasso'] = "Ray-traced 3D";
DuAEComp.RendererNames['ADBE Ernst'] = "Cinema 4D";

/**
 * Converts the number of frames to the time in seconds
 * @param {int} frames The frames
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp
 * @returns {float} The time, in seconds
 */
DuAEComp.framesToTime = function(frames, comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return 0;

    return frames * comp.frameDuration;
}

/**
 * Converts the time in seconds to the number of frames
 * @param {float} [time=comp.time] The time in seconds
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp
 * @returns {int} The number of frames, rounded
 */
DuAEComp.timeToFrames = function(time, comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return 0;
    time = def(time, comp.time);

    return Math.round(time / comp.frameDuration);
}

/**
 * Replaces text in Expressions
 * @param {string} oldString - The string to replace
 * @param {string} newString - The new string
 * @param {boolean} [caseSensitive=true] - Whether the search has to be case sensitive
 * @param {boolean} [selectedLayers=false] Set to true to cache only selected layers.
 * @param {CompItem} [comp=DuAEProject.getActiveComp()] The comp with expressions to cache.
 */
DuAEComp.replaceInExpressions = function(oldString, newString, caseSensitive, selectedLayers) {
    var comp = DuAEProject.getActiveComp();
    if (!comp) return;

    caseSensitive = def(caseSensitive, true);
    selectedLayers = def(selectedLayers, false);

    var selectionMode = DuAE.SelectionMode.SELECTED_LAYERS;
    if (!selectedLayers) selectionMode = DuAE.SelectionMode.ACTIVE_COMPOSITION;

    var re = new RegExp(DuRegExp.escape(oldString), caseSensitive ? 'g' : 'gi');

    DuAEExpression.doInExpresssions(function(e) {
        e.changed = re.test(e.expression);
        if (e.changed) e.expression = e.expression.replace(re, newString);
        return e;
    }, selectionMode );
}

/**
 * Replace all <code>thisComp</code> occurences by <code>comp("name")</code>.
 * @param {DuAE.SelectionMode} [selectionMode=DuAE.ACTIVE_COMPOSITION] The comp(s)/layers/properties to use.
 */
DuAEComp.removeThisCompInExpressions = function(selectionMode) {
    selectionMode = def(selectionMode, DuAE.ACTIVE_COMPOSITION);

    var re = new RegExp("(^\\s*|" + DuRegExp.javascriptSymbols + ")thisComp(?!" + DuRegExp.javascriptVarChars + ")", 'gm')

    DuAEExpression.doInExpresssions(function(e) {
        e.changed = re.test(e.expression);
        if (e.changed) e.expression = e.expression.replace(re, '$1comp("' + e.comp.name + '")');
    }, selectionMode);
}

/**
 * Replace all <code>comp("name")</code> occurences by <code>thisComp</code>.
 * @param {DuAE.SelectionMode} [selectionMode=DuAE.ACTIVE_COMPOSITION] The comp(s)/layers/properties to use.
 */
DuAEComp.removeCompInExpressions = function(selectionMode) {
    selectionMode = def(selectionMode, DuAE.ACTIVE_COMPOSITION);

    DuAEExpression.doInExpresssions(function(e) {
        var name = e.comp.name;
        var re = new RegExp("(^\\s*|" + DuRegExp.javascriptSymbols + ")comp\\s*\\(\\s*[\"']" + DuRegExp.escape(name) + "[\"']\\s*\\)", 'gm');
        e.changed = re.test(e.expression);
        if (e.changed) e.expression = e.expression.replace(re, '$1thisComp');
    }, selectionMode);
}

/**
 * Replace all <code>thisLayer</code> occurences by <code>layer("name")</code>.
 * @param {DuAE.SelectionMode} [selectionMode=DuAE.ACTIVE_COMPOSITION] The comp(s)/layers/properties to use.
 */
DuAEComp.removeThisLayerInExpressions = function(selectionMode) {
    selectionMode = def(selectionMode, DuAE.ACTIVE_COMPOSITION);

    var re = new RegExp("(^\\s*|" + DuRegExp.javascriptSymbols + ")thisLayer(?!" + DuRegExp.javascriptVarChars + ")", 'gm')

    DuAEExpression.doInExpresssions(function(e) {
        e.changed = re.test(e.expression);
        if (e.changed) e.expression = e.expression.replace(re, '$1thisComp.layer("' + e.layer.name + '")');
    }, selectionMode);
}

/**
 * Replace all <code>comp("name")</code> occurences by <code>thisComp</code>.
 * @param {DuAE.SelectionMode} [selectionMode=DuAE.ACTIVE_COMPOSITION] The comp(s)/layers/properties to use.
 */
DuAEComp.removeLayerInExpressions = function(selectionMode) {
    selectionMode = def(selectionMode, DuAE.ACTIVE_COMPOSITION);

    DuAEExpression.doInExpresssions(function(e) {
        var name = e.layer.name;
        var re = new RegExp("(^\\s*|" + DuRegExp.javascriptSymbolsNoDot + ")(thisComp.)?layer\\s*\\(\\s*[\"']" + DuRegExp.escape(name) + "[\"']\\s*\\)", 'gm');
        e.changed = re.test(e.expression);
        if (e.changed) e.expression = e.expression.replace(re, '$1thisLayer');
    }, selectionMode);
}

/**
 * Makes sure the composition has a unique name, renaming it if needed.
 * @param {CompItem} comp - The composition
 * @return {string} The new name.
 */
DuAEComp.setUniqueCompName = function(comp) {
    if (!(comp instanceof CompItem)) throw "Cannot set a unique composition name, this is not a composition.";
    //temporarily rename the comp to check for its name
    var newName = comp.name;
    comp.name = '***DuAEF-temp-name-xxx***';
    comp.name = DuAEProject.newUniqueCompName(newName, false);
    app.project.autoFixExpressions(newName, comp.name);
    return comp.name;
}

/**
 * Makes sure all layers in the comp have unique names, renaming them if needed.
 * @param {Array|LayerCollection} [layers=comp.layers] - The layers
 * @param {CompItem} [comp=DuAEProject.getActiveComp] - The composition
 */
DuAEComp.setUniqueLayerNames = function(layers, comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    layers = def(layers, comp.layers);
    var it = new DuList(layers);
    it.do(function(layer) {
        var locked = layer.locked;
        layer.locked = false;
        //temporarily set another name to correctly generate a new unique name 
        var oldName = layer.name;
        layer.name = '***Duik-temp-name-xxx***';
        layer.name = DuAEComp.newUniqueLayerName(oldName, comp);
        app.project.autoFixExpressions(oldName, layer.name);
        layer.locked = locked;
    });
}

/**
 * Gets the After Effects selected properties in the current comp
 * @param {PropertyType|PropertyValueType|string|function}	 [filter]	- A filter to get only a certain type, or value type, or property name or matchName.<br />
 * A function which take one PropertyBase as argument can be used to filter the properties: the Property will be returned if the function returns true.
 * @param {boolean}	[strict=false]	- If a string filter is provided, whether to search for the exact name/matchName or if it contains the filter.
 * @param {boolean}	[caseSensitive=true]	- If a string filter is provided, and not strict is false, does the search have to be case sensitive?
 * @return {DuAEProperty[]} The selected properties, an empty Array if nothing active or selected
 */
DuAEComp.getSelectedProps = function(filter, strict, caseSensitive) {
    var props = [];
    var comp = DuAEProject.getActiveComp();
    if (!comp) return props;

    //if no filter, get all using AE native API
    if (!isdef( filter )) {
        var layers = comp.selectedLayers;
        var itLayers = new DuList(layers);
        itLayers.do(function(layer) {
            props = props.concat(layer.selectedProperties);
        });
    } else {
        var layers = comp.selectedLayers;
        if (layers.length === 0) return props;

        for (var i = 0, numL = layers.length; i < numL; i++) {
            props = props.concat(DuAELayer.getSelectedProps(layers[i], filter, strict, caseSensitive));
        }
    }

    return DuAE.getDuAEProperty(props);
}

/**
 * Gets the first selected property (which is not a group)
 * @param {CompItem} [comp] The comnposition. The active composition by default.
 * @return {DuAEProperty|null} The selected property.
 */
DuAEComp.getSelectedProperty = function(comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return null;

    var props = comp.selectedProperties;
    if (props.length == 0) return null;
    var prop = null;
    for (var i = 0, n = props.length; i < n; i++) {
        var p = props[i];
        if (p instanceof Property) {
            prop = p;
            break;
        }
    }
    return new DuAEProperty(prop);
}

/**
 * Gets the selected layers in the current comp
 * @return {Layer[]} The selected layers
 */
DuAEComp.getSelectedLayers = function() {
    var comp = DuAEProject.getActiveComp();
    if (!comp) return [];
    return comp.selectedLayers;
}

/**
 * Gets the first selected layer in the After Effects current composition
 * @return {Layer|null} The layer or null if there's no current comp / no selected layer
 */
DuAEComp.getActiveLayer = function() {
    var layers = DuAEComp.getSelectedLayers();
    return layers[0];
}

/**
 * Runs a function on all the layers
 * @param {function} method - The function to run on the layers, which takes a layer as its only argument.
 * @param {CompItem} [comp] - The comp containing the layers. Will use the current comp if not provided.
 * @param {Bool} [reverse=false] - Set this to true to iterate from the end.
 */
DuAEComp.doLayers = function(method, comp, reverse) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!(comp)) return;

    //get layers
    var it = new DuList(comp.layers);
    it.do( method, reverse );
}

/**
 * Gets the After Effects animated (with keyframes) properties in the current comp
 * @param {PropertyType|PropertyValueType|string|function}	 [filter]	- A filter to get only a certain type, or value type, or property name or matchName.<br />
 * A function which take one PropertyBase as argument can be used to filter the properties: the Property will be returned if the function returns true.
 * @param {boolean}	[strict=false]	- If a string filter is provided, whether to search for the exact name/matchName or if it contains the filter.
 * @param {boolean}	[caseSensitive=true]	- If a string filter is provided, and not strict is false, does the search have to be case sensitive?
 * @param {boolean}	[selectedLayersOnly=false]	- True to get the properties on the selected layers only
 * @param {CompItem}	[comp=DuAEProject.getActiveComp]	- The composition
 * @return {DuAEProperty[]} The selected properties, an empty Array if nothing active or selected
 */
DuAEComp.getAnimatedProps = function(filter, strict, caseSensitive, selectedLayersOnly, comp) {
    var props = [];
    if (!isdef( comp )) comp = DuAEProject.getActiveComp();
    if (!comp) return props;

    selectedLayersOnly = def(selectedLayersOnly, false);


    var layers;
    if (selectedLayersOnly) layers = comp.selectedLayers;
    else layers = comp.layers;

    var it = new DuList(layers);
    it.do(function(layer) {
        layer = new DuAEProperty(layer);
        props = props.concat(layer.getAnimatedProps(filter, strict, caseSensitive));
    });

    return props;
}

/**
 * Deselects all properties in the current composition
 */
DuAEComp.unselectProperties = function() {
    var comp = DuAEProject.getActiveComp();
    if (!comp) return props;
    var props = comp.selectedProperties;
    for (var i = 0; i < props.length; i++) {
        props[i].selected = false;
    }
}

/**
 * Deselects all layers in a composition
 * @param {CompItem} [comp=app.project.activeItem] - The composition
 * @return {Layer[]} The previously selected layers.<br />
 * A custom attribute, Layer.props is added on each layer object which is an array of all previously selected properties as DuAEProperty objects
 */
DuAEComp.unselectLayers = function(comp) {
    if (!isdef( comp )) comp = DuAEProject.getActiveComp();
    if (!comp) return [];

    var layers = [];

    if (!comp) return layers;
    if (!(comp instanceof CompItem)) return layers;

    layers = [];

    while (comp.selectedLayers.length > 0) {
        var layer = comp.selectedLayers[0];
        layer.props = DuAE.getDuAEProperty(layer.selectedProperties);
        layer.selected = false;
        layers.push(layer);
    }

    return layers;
}

/**
 * Selects the layers
 * @param {Layer[]|DuList.<Layer>} layers - The layers
 */
DuAEComp.selectLayers = function(layers) {
    new DuList(layers).do(function(layer) {
        if (layer == undefined) return;
        if (layer == null) return;
        layer.selected = true;
    });
}

/**
 * Generates a new unique name for a layer
 * @param {string} newName	- The wanted new name
 * @param {CompItem} [comp] 	- The comp
 * @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.
 */
DuAEComp.newUniqueLayerName = function(newName, comp, increment) {
    // Ignore the last block
    // Deactivated - Why did I do that in the first place??
    // Answer: because I wanted the number not to be at the end... Which was a very bad idea
    /*var nName = newName.split(' | ');
    var lastBlock = '';
    if (nName.length == 4) {
        lastBlock = nName.pop();
        newName = nName.join(' | ');
    }//*/

    // Let's try to remove any trailing number first, that's better
    newName = DuString.trimNumbers(newName);

    increment = def(increment, true);
    comp = def(comp, DuAEProject.getActiveComp());
    var layerNames = [];
    // Check if we really need to change the name
    var isAlreadyUnique = true;
    for (var i = 1; i <= comp.layers.length; i++) {
        var n = comp.layer(i).name;
        if (n == newName) isAlreadyUnique = false;
        // Ignore the last block
        /*var tName = n.split(' | ');
        if (tName.length == 4) {
            tName.pop();
            n = tName.join(' | ');
        }//*/
        layerNames.push(n);
    }
    // Let's stop here if there's no need to change it
    if (isAlreadyUnique) return newName;

    newName = DuString.generateUnique(newName, layerNames, increment);
    // Ignore the last block
    //if (lastBlock != '') newName += ' | ' + lastBlock;
    return newName;
}

/**
 * Generates a new unique name for a marker for this comp
 * @param {string} newName	- The wanted new name
 * @param {CompItem} comp 	- The comp
 * @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.
 */
DuAEComp.newUniqueMarkerName = function(newName, comp, increment) {
    if (increment == undefined) increment = true;
    var markerNames = [];
    for (var i = 1, num = comp.markerProperty.numKeys; i <= num; i++) {
        markerNames.push(comp.markerProperty.keyValue(i).comment);
    }
    return DuString.generateUnique(newName, markerNames, increment);
}

/**
 * Creates a new Adjustment layer
 * @param {CompItem} comp 	- The comp
 * @return {AVLayer}	The layer.
 */
DuAEComp.addAdjustmentLayer = function(comp) {
    if (comp == undefined) return null;
    var layer = comp.layers.addSolid([1, 1, 1], DuAEComp.newUniqueLayerName("Adjustment Layer", comp), comp.width, comp.height, comp.pixelAspect, comp.duration);
    layer.adjustmentLayer = true;
    return layer;
}

/**
 * Links all orphan layers in the comp to a layer
 * @param {Layer} layer - The parent layer
 * @param {bool} [includeLockedLayers=false] - True to parent layers even if they are locked
 */
DuAEComp.parentAllOrphans = function(layer, includeLockedLayers) {
    includeLockedLayers = def(includeLockedLayers, false);
    var comp = layer.containingComp;
    for (var i = 1, num = comp.numLayers; i <= num; i++) {
        if (i == layer.index) continue;
        var l = comp.layer(i);
        if (DuAELayer.getRelation(l, layer) < 0) continue;
        var locked = l.locked;
        if (locked && includeLockedLayers) l.locked = false;
        if (!locked && l.parent == null) l.parent = layer;
        if (locked && includeLockedLayers) l.locked = locked;
    }
}

/**
 * Gets all precomps and parent comps of the composition
 * @param {CompItem} [comp=DuAEProject.getActiveComp()] - The composition
 * @param {bool} [recursive=false] - True to search to more than one level of precomposition
 * @return {CompItem[]} The related compositons
 */
DuAEComp.getRelatives = function(comp, recursive) {
    recursive = def(recursive, false);
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return [];
    //get precomps
    var precomps = DuAEComp.getPrecomps(comp, recursive);
    //get parent
    var parentComps = [];
    if (recursive) {
        parentComps = DuAEComp.getParentComps(comp);
    } else {
        parentComps = comp.usedIn;
    }

    return precomps.concat(parentComps);
}

/**
 * Gets all the precomposition found in the comp.
 * @param {CompItem} [comp] - The composition. The active composition if ommitted.
 * @param {bool} [recursive=false] - True to get nested compositions
 * @return {DuList.<CompItem>} The precompositions
 */
DuAEComp.getPrecomps = function(comp, recursive) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return new DuList();
    recursive = def(recursive, true);
    var precomps = new DuList();
    var it = new DuList(comp.layers);
    it.do(function(layer) {
        var precomp = layer.source;
        if (precomp instanceof CompItem) {
            precomps.push(precomp);
            if (recursive) precomps = precomps.concat(DuAEComp.getPrecomps(precomp));
        }
    });
    //remove duplicates
    precomps.removeDuplicates();
    return precomps;
}

/**
 * Recursively gets all compositions where this item is used
 * @param {AVItem} item - The item
 * @return {CompItem[]} The compositions
 */
DuAEComp.getParentComps = function(item) {
    var parentComps = item.usedIn;
    new DuList(parentComps).do(function(parentComp) {
        parentComps = parentComps.concat(DuAEComp.getParentComps(parentComp));
    });
    parentComps = new DuList(parentComps);
    parentComps.removeDuplicates();
    return parentComps;
}

/**
 * Gets all the layers with audio in the composition
 * @param {CompItem}	 comp	The composition where the audio will be searched
 * @param {bool}	[audioActiveOnly=false]	 If true, does not get muted layers.
 * @return {AVLayer[]} An array of AVLayer containing the audio layers
 */
DuAEComp.getAudioLayers = function(comp, audioActiveOnly) {
    audioActiveOnly = def(audioActiveOnly, false);
    var layers = comp.layers;
    var audioLayers = [];
    var it = new DuList(layers);
    it.do(function(layer) {
        if (layer.hasAudio) {
            if (audioActiveOnly && layer.audioEnabled || !audioActiveOnly) {
                audioLayers.push(layer);
            }
        }
    });

    return audioLayers;
}

/**
 * Gets the total number of master properties used on precompositions in the comp.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to check
 * @return {int} The number of master properties
 */
DuAEComp.numMasterProperties = function(comp) {
    if (DuAE.version.version < 15.1) return 0;
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return 0;
    var numMP = 0;
    for (var i = 1, n = comp.numLayers; i <= n; i++) {
        var l = comp.layer(i);
        if (!l.source) continue;
        if (l.source instanceof CompItem) {
            numMP += l('ADBE Layer Overrides').numProperties;
        }
    }
    return numMP;
}

/**
 * Checks if all layers have a different name.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp to check
 * @return {Object} The list of names used several times. Check the length attribute to know how many duplicates were found, loop through the keys to get the names. Eech key is an array containing the list of layers with that name.
 * @example
 * var dupes = DuAEComp.checkLayerNames();
 * if (dupes.length != 0) {
 * for (name in dupes)
 * {
 *     if (dupes.hasOwnProperty(name)) alert(dupes[name]); //dupes[name] is an array of Layer
 * }
 * }
 */
DuAEComp.checkLayerNames = function(comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    var duplicatedNames = {};
    duplicatedNames.length = 0;
    if (!comp) return duplicatedNames;
    var layerNames = {};
    var layers = app.project.items;
    for (var i = 1, n = comp.numLayers; i <= n; i++) {
        var layer = comp.layer(i);
        var name = layer.name;

        if (duplicatedNames.hasOwnProperty(name)) {
            duplicatedNames[name].push(layer);
            continue;
        }

        if (layerNames.hasOwnProperty(name)) {
            duplicatedNames[name] = [layerNames[name], layer];
            duplicatedNames.length++;
            continue;
        }
        layerNames[name] = layer;
    }
    return duplicatedNames;
}

/**
 * Creates a new "Shape Null" in the comp.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp where to create the layer
 * @param {float} [size=100] The size of the null
 * @param {Layer} [layer] A layer for the location of the null
 * @returns {ShapeLayer} The null layer
 */
DuAEComp.addNull = function(comp, size, layer) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    size = def(size, 100);
    layer = def(layer, null);

    var nullLayer = comp.layers.addShape();
    nullLayer.guideLayer = true;
    nullLayer.name = i18n._("Null");
    nullLayer.label = 1;

    DuAELayer.applyPreset(nullLayer, preset_null.toFile());

    if (layer != null) {
        layerParent = layer.parent;
        layer.parent = null;
        nullLayer.transform.position.setValue(layer.transform.position.value);
        layer.parent = layerParent;
        nullLayer.moveBefore(layer);
        nullLayer.name = "Null " + layer.name;
    }

    DuAELayer.setType(DuAELayer.Type.NULL, nullLayer);

    return nullLayer;
}

/**
 * Creates a new "Shape Adjustment Layer" in the comp.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp where to create the layer
 * @returns {ShapeLayer} The adjustment layer
 */
DuAEComp.addAdjustmentLayer = function(comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;

    var layer = comp.layers.addShape();
    layer.name = i18n._("Adjustment layer");
    layer.adjustmentLayer = true;
    layer.label = 5;

    var solidGroup = layer("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
    solidGroup.name = 'Solid';
    var solidContent = solidGroup.property("ADBE Vectors Group");
    var solid = solidContent.addProperty("ADBE Vector Shape - Rect");
    solid('ADBE Vector Rect Size').setValue([comp.width, comp.height]);
    var fill = solidContent.addProperty("ADBE Vector Graphic - Fill");
    fill("ADBE Vector Fill Color").setValue([1, 1, 1, 1]);

    DuAELayer.setType(DuAELayer.Type.ADJUSTMENT, layer);

    return layer;
}

/**
 * Creates a new "Solid Adjustment Layer" in the comp.
 * @param {DuColor} [color=DuColor.Color.RAINBOX_RED] The color of the solid
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp where to create the layer
 * @returns {ShapeLayer} The adjustment layer
 */
DuAEComp.addSolid = function(color, comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;

    color = def(color, DuColor.Color.RAINBOX_RED);
    if (!(color instanceof DuColor)) color = new DuColor(color);

    var layer = comp.layers.addShape();
    layer.name = i18n._("Solid");
    layer.label = 1;

    var solidGroup = layer("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
    solidGroup.name = 'Solid';
    var solidContent = solidGroup.property("ADBE Vectors Group");
    var solid = solidContent.addProperty("ADBE Vector Shape - Rect");
    solid('ADBE Vector Rect Size').setValue([comp.width, comp.height]);
    var fill = solidContent.addProperty("ADBE Vector Graphic - Fill");
    fill("ADBE Vector Fill Color").setValue(color.floatRGBA());

    DuAELayer.setType(DuAELayer.Type.SOLID, layer);

    return layer;
}

/**
 * Creates a new Shape Layer in the comp.
 * @param {DuAEShapeLayer.Primitive} [shape=DuAEShapeLayer.Primitive.NONE] The shape
 * @param {DuColor} [color=DuColor.Color.RAINBOX_RED] The color of the shape
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The comp where to create the layer
 * @returns {ShapeLayer} The adjustment layer
 */
DuAEComp.addShape = function(shape, color, comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;

    shape = def(shape, DuAEShapeLayer.Primitive.NONE);
    color = def(color, DuColor.Color.RAINBOX_RED);

    var layer = comp.layers.addShape();
    layer.name = i18n._("Shape");

    if (shape == DuAEShapeLayer.Primitive.NONE) return layer;

    var solidGroup = layer("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
    var solidContent = solidGroup.property("ADBE Vectors Group");

    var size = comp.height / 3;

    if (shape == DuAEShapeLayer.Primitive.SQUARE) {
        solidGroup.name = i18n._("Rectangle");
        layer.name = i18n._("Rectangle");
        var solid = solidContent.addProperty("ADBE Vector Shape - Rect");
        solid('ADBE Vector Rect Size').setValue([size, size]);
    } else if (shape == DuAEShapeLayer.Primitive.ROUNDED_SQUARE) {
        solidGroup.name = i18n._("Rounded rectangle");
        layer.name = i18n._("Rounded rectangle");
        var solid = solidContent.addProperty("ADBE Vector Shape - Rect");
        solid('ADBE Vector Rect Size').setValue([size, size]);
        solid('ADBE Vector Rect Roundness').setValue(size / 10);
    } else if (shape == DuAEShapeLayer.Primitive.CIRCLE) {
        solidGroup.name = i18n._("Circle");
        layer.name = i18n._("Circle");
        var solid = solidContent.addProperty("ADBE Vector Shape - Ellipse");
        solid('ADBE Vector Ellipse Size').setValue([size, size]);
    } else if (shape == DuAEShapeLayer.Primitive.POLYGON) {
        solidGroup.name = i18n._("Polygon");
        layer.name = i18n._("Polygon");
        var solid = solidContent.addProperty("ADBE Vector Shape - Star");
        solid('ADBE Vector Star Type').setValue(2);
        solid('ADBE Vector Star Outer Radius').setValue(size);
    } else if (shape == DuAEShapeLayer.Primitive.STAR) {
        solidGroup.name = i18n._("Star");
        layer.name = i18n._("Star");
        var solid = solidContent.addProperty("ADBE Vector Shape - Star");
        solid('ADBE Vector Star Inner Radius').setValue(size * .385);
        solid('ADBE Vector Star Outer Radius').setValue(size);
    }

    var fill = solidContent.addProperty("ADBE Vector Graphic - Fill");
    fill("ADBE Vector Fill Color").setValue(DuColor.Color.APP_HIGHLIGHT_COLOR.floatRGBA());

    return layer;
}

/**
 * Saves a thumbnail of the comp to a PNG file
 * @param {File} file The file to save the thumbnail
 * @param {int[]} [maxRes=[500,500]] The maximum resolution of the thumbnail, which will be smaller than that, but not exactly this size.
 * @param {float} [time] The time at which to grab the picture. If omitted, will use the current time.
 * @param {CompItem} [comp=DuAEProject.getActiveComp()] The composition
 * @returns {Boolean} True on success, false otherwise.
 */
DuAEComp.thumbnail = function(file, maxRes, time, comp) {
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return false;

    maxRes = def(maxRes, [500, 500]);
    time = def(time, comp.time);

    // Downsample
    var currentResolution = comp.resolutionFactor;

    var newFactor = Math.ceil(comp.width / maxRes[0]);
    var test = Math.ceil(comp.height / maxRes[1]);
    if (test > newFactor) newFactor = test;

    comp.resolutionFactor = [newFactor, newFactor];

    comp.saveFrameToPng(time, file);

    // Restore
    comp.resolutionFactor = currentResolution;

    return true;
}

/**
 * Gets the camera in the comp, in the given layers if possible.
 * @param {Layer[]|DuList.<Layer>} [layers] Some layers to find the camera first. Selected layers if omitted.
 * @return {CameraLayer|null} The camera if it was found.
 */
DuAEComp.camera = function(layers) {
    layers = def(layers, DuAEComp.getSelectedLayers());
    layers = new DuList(layers);

    // Search in selected layers
    var layer;
    while (layer = layers.next()) {
        if (layer instanceof CameraLayer) return layer;
    }

    // Get composition
    var comp;
    if (layers.isEmpty()) comp = DuAEProject.getActiveComp();
    else comp = layers.at(0).containingComp;
    if (!comp) return null;

    // Search in all comp layers
    for (var i = 1, n = comp.numLayers; i <= n; i++) {
        var l = comp.layer(i);
        if (l instanceof CameraLayer) return l;
    }

    return null;
}

/**
 * Crops a composition
 * @param {float[]} bounds The bounds [top, left, width, height]
 * @param {CompItem} [comp] The composition. The active composition by default.
 */
DuAEComp.crop = function(bounds, comp) {
    comp = def(comp, DuAEProject.getActiveComp());

    // Add Null to move everybody
    var n = DuAEComp.addNull(comp);
    n.transform.position.setValue([0, 0]);
    DuAEComp.parentAllOrphans(n, true);
    // Offset
    n.transform.position.setValue([-bounds[1], -bounds[0]]);
    // Remove
    n.remove();

    // Resize
    comp.width = parseInt(bounds[2]);
    comp.height = parseInt(bounds[3]);
}

/**
 * Bakes the expressions to keyframes.
 * @param {DuAEExpression.BakeAlgorithm} [mode=DuAEExpression.BakeAlgorithm.SMART] The algorithm to use for baking the expressions.
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 * @param {CompItem} [comp] The composition. The active composition by default.
 */
DuAEComp.bakeExpressions = function(mode, frameStep, comp) {
    DuAEComp.doLayers(function(layer) {
        var l = new DuAEProperty(layer);
        l.bakeExpressions(mode, frameStep);
    }, comp);
}

/**
 * Bakes the expressions to keyframes and removes all non-renderable layers.
 * @param {DuAEExpression.BakeAlgorithm} [mode=DuAEExpression.BakeAlgorithm.SMART] The algorithm to use for baking the expressions.
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 * @param {CompItem} [comp] The composition. The active composition by default.
 */
DuAEComp.bake = function( mode, frameStep, comp ) {
    // Bake all layers
    DuAEComp.doLayers(function(layer) {
        // Don't bake non-renderable layers
        if (!DuAELayer.isRenderable(layer)) return;
        DuAELayer.bake( mode, frameStep, layer );
    }, comp, true);

    // Remove non-renderable layers
    DuAEComp.doLayers(function(layer) {
        if ( !DuAELayer.isRenderable(layer) ) { 
            layer.locked = false;
            layer.remove();
        }
    }, comp, true)
}

/**
 * Updates the composition settings
 * @param {Object} settings The settings to update.
 * @param {bool} [updatePrecomps=true] Set to false to update only the selected/current comp
 * @param {CompItem[]} [comps] The compositions to update. If omitted, will update either the selected items in the project or the current composition
 */
DuAEComp.updateSettings = function( settings, updatePrecomps, comps ) {
    updatePrecomps = def(updatePrecomps, true);
    
    if (!isdef( comps )) {
        // Active comp
        if ((app.project.activeItem !== null) && (app.project.activeItem instanceof CompItem))
		{
			comps = [app.project.activeItem];
		}
		else // Or selection
		{
			comps = proj.selection;
		}
    }
    if (comps.length == 0) return;

    // Keep the ones which are updated
    var okIds = new DuList();
    // Aaaand GO!
    for (var i = 0, n = comps.length; i < n; i++)
    {
        var comp = comps[i];
        // Not a comp
        if (!(comp instanceof CompItem)) continue;
        // Already updated
        if (okIds.contains(comp.id)) continue;

        okIds.push(comp.id);

        if (updatePrecomps) {
            // Get precomps to update them
            var precomps = DuAEComp.getPrecomps(comp, true);
            var precompsToUpdate = [];
            precomps.do(function(precomp) {
                if (okIds.contains(precomp.id)) return;
                precompsToUpdate.push( precomp );
                okIds.push( precomp.id );
            });
            DuAEComp.updateSettings( settings, false, precompsToUpdate);
        }

        // Resize
        var w = typeof settings.width !== 'undefined';
        var h = typeof settings.height !== 'undefined';
        if (w || h) {
            settings.anchor = def(settings.anchor, DuMath.Location.CENTER);
            // Add null
            var z = DuAEComp.addNull(comp);
            // Parent
            DuAELayer.parent( comp.layers, z, true );
            var offset = [0, 0];
            if (w) {
                offset[0] = settings.width - comp.width;
                if (DuMath.isLocationHCenter( settings.anchor )) offset[0] /= 2;
                if (DuMath.isLocationLeft( settings.anchor )) offset[0] = 0;
                comp.width = settings.width;
            }
            if (h) {
                offset[1] = settings.height - comp.height;
                if (DuMath.isLocationVCenter( settings.anchor )) offset[1] /= 2;
                if (DuMath.isLocationTop( settings.anchor )) offset[1] = 0;
                comp.height = settings.height;
            }
            DuAELayer.translate( z, offset );
            z.remove();
        }
        
        if (typeof settings.pixelAspect !== 'undefined') comp.pixelAspect = settings.pixelAspect;
        if (typeof settings.frameRate !== 'undefined') comp.frameRate = settings.frameRate;
        if (typeof settings.duration !== 'undefined') {
            var newDuration = currentFormatToTime( settings.duration, comp.frameRate, true );
            // We're also going to (try to) set a new duration for all layers ending with the comp
            if (comp.duration < newDuration) {
                for (var l = 1, ln = comp.numLayers; l <= ln; l++) {
                    var layer = comp.layer(l);
                    var locked = layer.locked;
                    layer.locked = false;
                    if (layer.outPoint >= comp.duration) layer.outPoint = newDuration;
                    layer.locked = locked;
                }
            }
            comp.duration = newDuration;
        }
        if (typeof settings.resolutionFactor !== 'undefined') comp.resolutionFactor = settings.resolutionFactor;
        if (typeof settings.preserveNestedResolution !== 'undefined') comp.preserveNestedResolution = settings.preserveNestedResolution;
        if (typeof settings.bgColor !== 'undefined') comp.bgColor = settings.bgColor;
        //if (typeof settings.draft3D !== 'undefined') comp.draft3D = settings.draft3D;
        if (typeof settings.hideShyLayers !== 'undefined') comp.hideShyLayers = settings.hideShyLayers;
        if (typeof settings.useProxy !== 'undefined' && comp.proxySource) comp.useProxy = settings.useProxy;
        if (typeof settings.renderer !== 'undefined') comp.renderer = settings.renderer;
        if (typeof settings.preserveNestedFrameRate !== 'undefined') comp.preserveNestedFrameRate = settings.preserveNestedFrameRate;
        if (typeof settings.frameBlending !== 'undefined') comp.frameBlending = settings.frameBlending;
        if (typeof settings.motionBlur !== 'undefined') comp.motionBlur = settings.motionBlur;
        if (typeof settings.shutterAngle !== 'undefined') comp.shutterAngle = settings.shutterAngle;
        if (typeof settings.shutterPhase !== 'undefined') comp.shutterPhase = settings.shutterPhase;
        if (typeof settings.motionBlurSamplesPerFrame !== 'undefined') comp.motionBlurSamplesPerFrame = settings.motionBlurSamplesPerFrame;
        if (typeof settings.motionBlurAdaptiveSampleLimit !== 'undefined') comp.motionBlurAdaptiveSampleLimit = settings.motionBlurAdaptiveSampleLimit;
    }
}// ==================== |-------| ====================
// ==================== | layer | ====================
// ==================== |-------| ====================

/**
 * After Effects layer methods
 * @namespace
 * @category DuAEF
 */
var DuAELayer = {};

DuAELayer.Type = {
    NONE: '-',
    NULL: 'Null',
    SOLID: 'Solid',
    ADJUSTMENT: 'Adjustment'
}

/**
 * Checks if a string is one of the prefixes used to identify layer types in their names
 * @param {string} prefix The string to check
 * @return {bool} True if the string is one of the predefined prefixes.
 */
DuAELayer.isTypePrefix = function( prefix ) {
    for (i in DuAELayer.Type) {
        if (prefix == DuAELayer.Type[i]) return true;
    }
    return false;
}

/**
 * Checks if the layer is one of the types created by duaef.
 * @param {Layer} layer - The layer to check
 * @param {Duik.Layer.Type} layerType - The type of layer
 * @return {Boolean}
 */
DuAELayer.isType = function(layer, layerType) {
    return DuAELayer.type(layer) == layerType;
}

/**
 * Gets the type of the layer
 * @param {Layer} [layer] The layer. If omitted, will check the first selected bone of the current comp
 * @returns {Duik.Layer.Type} The type
 */
DuAELayer.type = function(layer) {
    layer = def(layer, DuAEComp.getActiveLayer());
    if (!layer) return DuAELayer.Type.NONE;
    var type = DuAETag.getValue(layer, DuAETag.Key.LAYER_TYPE, DuAETag.Type.STRING);
    if (type == null) return DuAELayer.Type.NONE;
    return type;
}

/**
 * Sets the type of the layer
 * @param {Duik.Layer.Type} type The type
 * @param {Layer[]|LayerCollection|DuList.<Layer>|Layer} [layers=DuAEComp.getSelectedLayers()] The layer. If omitted, will use all selected layers in the comp
 */
DuAELayer.setType = function(type, layers) {
    layers = def(layers, DuAEComp.getSelectedLayers());
    layers = new DuList(layers);
    if (layers.length() == 0) return;

    for (var i = 0, n = layers.length(); i < n; i++) {
        var layer = layers.at(i);
        
        DuAETag.setValue(layer, DuAETag.Key.LAYER_TYPE, type);

        // Reset
        layer.guideLayer = false;
        layer.label = 8;
        layer.quality = LayerQuality.BEST;

        if (type == DuAELayer.Type.NULL) {
            layer.guideLayer = true;
            layer.label = 1;
        }
        else if (type == DuAELayer.Type.SOLID) {
            layer.label = 1;
        }
        else if (type == DuAELayer.Type.ADJUSTMENT) {
            layer.label = 5;
        }
    }
}

/**
 * Runs a function on all the layers
 * @param {function} method - The function to run on the layers, which takes a layer as its only argument.
 * @param {string} [undoGroupName] - The name of the undoGroup created before the execution. If not provided, there will not be any undoGroup created.
 */
DuAELayer.doLayers = function(layers, method, undoGroupName) {
    undoGroupName = def(undoGroupName, "");
    // run
    if (undoGroupName != "") DuAE.beginUndoGroup(undoGroupName);
    new DuList(layers).do(method);
    if (undoGroupName != "") DuAE.endUndoGroup();
}

/**
 * Generates a new unique name for an effect
 * @param {string} newName	- The wanted new name
 * @param {Layer} layer 	- The layer
 * @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.
 */
DuAELayer.newUniqueEffectName = function(newName, layer, increment) {
    if (!layer) {
        DuDebug.throwUndefinedError('layer', 'DuAELayer.newUniqueEffectName');
        return;
    }
    if (!isdef( newName )) {
        DuDebug.throwUndefinedError('newName', 'DuAELayer.newUniqueEffectName');
        return;
    }
    increment = def(increment, true);
    if (newName == "") return "";
    var effectNames = [];
    for (var i = 1, numP = layer.effect.numProperties; i <= numP; i++) {
        effectNames.push(layer.effect(i).name);
    }
    return DuString.generateUnique(newName, effectNames, increment);
}

/**
 * Generates a new unique name for a marker for this layer
 * @param {string} newName	- The wanted new name
 * @param {Layer} layer 	- The layer
 * @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.
 */
DuAELayer.newUniqueMarkerName = function(newName, layer, increment) {
    increment = def(increment, true);
    var markerNames = [];
    for (var i = 1, num = layer.property('ADBE Marker').numKeys; i <= num; i++) {
        markerNames.push(layer.property('ADBE Marker').keyValue(i).comment);
    }
    return DuString.generateUnique(newName, markerNames, increment);
}

/**
 * Gets the After Effects selected properties in the layer
 * @param {Layer}	layer	- The layer
 * @param {PropertyType|PropertyValueType|string|function}	 [filter]	- A filter to get only a certain type, or value type, or property name or matchName.<br />
 * A function which take one PropertyBase as argument can be used to filter the properties: the Property will be returned if the function returns true.
 * @param {boolean}	[strict=false]	- If a string filter is provided, whether to search for the exact name/matchName or if it contains the filter.
 * @param {boolean}	[caseSensitive=true]	- If a string filter is provided, and not strict is false, does the search have to be case sensitive?
 * @return {DuAEProperty[]} The selected properties, an empty Array if nothing active or selected
 */
DuAELayer.getSelectedProps = function(layer, filter, strict, caseSensitive) {
    strict = def(strict, false);
    caseSensitive = def(caseSensitive, true);

    var props = [];

    if (!caseSensitive && typeof filter === "string") filter = filter.toLowerCase();

    var selectedProps = layer.selectedProperties;
    if (!isdef( filter )) {
        props = props.concat(selectedProps);
    } else {
        for (var j = 0, numP = selectedProps.length; j < numP; j++) {
            var prop = selectedProps[j];

            var name = prop.name;
            var matchName = prop.matchName;
            if (!caseSensitive) {
                name = name.toLowerCase();
                matchName = matchName.toLowerCase();
            }

            if (strict && name === filter) {
                props.push(prop);
            } else if (strict && matchName === filter) {
                props.push(prop);
            } else if (typeof filter === "string") {
                if (name.indexOf(filter) >= 0) props.push(prop);
                else if (matchName.indexOf(filter) >= 0) props.push(prop);
            } else if (prop.propertyType == PropertyType.PROPERTY) {
                if (prop.propertyValueType == filter) props.push(prop);
            } else if (props.length == 0 && filter == PropertyValueType.SHAPE) {
                if (matchName == 'ADBE Mask Atom') props.push(prop.property('ADBE Mask Shape'));
                else if (matchName == 'ADBE Vector Shape - Group') props.push(prop.property('ADBE Vector Shape'));
            } else if (prop.propertyType == filter) {
                props.push(prop);
            } else if (typeof filter === "function") {
                if (filter(prop)) props.push(prop);
            };
        }
    }
    return DuAE.getDuAEProperty(props);
}

/**
 * Gets the After Effects active property (the last selected one)
 * @param {Layer}	layer	- The layer
 * @return {DuAEProperty|null} The selected property, or null if there isn't any.
 */
DuAELayer.getActiveProperty = function(layer) {
    var selectedProps = layer.selectedProperties;
    if (selectedProps.length == 0) return null;
    var p = selectedProps[selectedProps.length -1 ];
    return new DuAEProperty(p);
}

/**
 * Gets all animations on the layer 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 {Layer}	layer	- The layer.
 * @param {Boolean}	[selected=false]	- true to get only selected keyframes.
 * @param {float[]}	[timeRange]	- The time range, an array of two time values, in seconds.
 * @return {DuAELayerAnimation}	The animation.
 */
DuAELayer.getAnim = function(layer, selected, timeRange) {
    var anim = new DuAELayerAnimation();
    anim._name = layer.name;
    anim._index = layer.index;
    anim.anims = [];
    for (var propIndex = 1; propIndex <= layer.numProperties; propIndex++) {
        var prop = layer.property(propIndex);
        if (prop.matchName == 'ADBE Marker') continue;

        prop = new DuAEProperty(prop);
        var subAnim = prop.animation(selected, timeRange);
        if (subAnim != null) {
            if (anim.startTime == null) anim.startTime = subAnim.startTime;
            else if (anim.startTime > subAnim.startTime) anim.startTime = subAnim.startTime;
            if (anim.endTime == null) anim.endTime = subAnim.endTime;
            else if (anim.endTime > subAnim.endTime) anim.endTime = subAnim.endTimeendTime;
            anim.anims.push(subAnim);
        }

    }
    return anim;
}

/**
 * Gets all animations on the layers 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 {Layer[]|LayerCollection}	layers	- The layers.
 * @param {Boolean}	[selected=false]	- true to get only selected keyframes.
 * @param {float[]}	[timeRange]	- The time range, an array of two time values, in seconds.
 * @return {DuAELayerAnimation[]}	The animations.
 */
DuAELayer.getAnims = function(layers, selected, timeRange) {
    var anims = [];
    new DuList(layers).do(function(layer) {
        anims.push(DuAELayer.getAnim(layer, selected, timeRange));
    });
    return anims;
}

/**
 * Sets the property animation on the property
 * @param {Layer}	layer	- The layer.
 * @param {DuAELayerAnimation} anims	- 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 name of the animation.<br />
 * This way, only the type of property (i.e. matchName) is checked.
 * @param {Boolean}	[setExpression=false]	- Set the expression on the property
 * @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[]}	[propertyWhiteList]	- 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.
 * @param {Boolean}	[offset=false]	- true to offset the current value, instead of replacing it
 * @param {Boolean}	[reverse=false]	- true to reverse the keyframes (in time)
 * @return {Boolean} true if the anim was actually set.
 */
DuAELayer.setAnim = function(layer, anim, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, reverse) {
    time = def(time, layer.containingComp.time);
    ignoreName = def(ignoreName, false);
    setExpression = def(setExpression, false);
    onlyKeyframes = def(onlyKeyframes, false);
    replace = def(replace, false);
    offset = def(offset, false);

    if (reverse) DuAELayer.reverseAnims(anim);

    for (var i = 0; i < anim.anims.length; i++) {
        var subAnim = anim.anims[i];
        for (var propIndex = 1; propIndex <= layer.numProperties; propIndex++) {
            var subProp = layer.property(propIndex);
            if (subProp == null) continue;
            if (subProp.matchName == subAnim._matchName && subProp.matchName != 'ADBE Marker') {
                subProp = new DuAEProperty(subProp);
                var ok = subProp.setAnimation(subAnim, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, reverse);
                if (ok) break;
            }
        }
    }
}

/**
 * Sets the animations on the layers.<br />
 * If you need to set only on the same layers (same index, same name), use {@link DuAELayer.setAnims}.
 * @param {Layer[]|LayerCollection}	layers	- The layers.<br />
 * If there are more layers than animations, the layers array will be truncated.
 * @param {DuAELayerAnimation[]} anims	- The layer animations.<br />
 * If there are more animations than layers, the animations array will be truncated.
 * @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 name animation.<br />
 * This way, only the type of property (i.e. matchName) is checked.
 * @param {Boolean}	[setExpression=false]	- Set the expression on the property
 * @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.
 * @param {Boolean}	[offset=false]	- true to offset the current value, instead of replacing it
 * @param {Boolean}	[reverse=false]	- true to reverse the keyframes (in time)<br />
 * Note: the remaining animations which are returned will already be reversed, do not set this to true again if you plan to set them later.
 */
DuAELayer.setAllAnims = function(layers, anims, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, reverse) {
    layers = new DuList(layers);
    var num = anims.length;
    if (num > layers.length()) num = layers.length();

    if (reverse) DuAELayer.reverseAnims(anims);

    layers.do(function(layer) {
        if (layer) DuAELayer.setAnim(layer, anims[layers.current], time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, false);
    });
}

/**
 * Sets the animations on the corresponding layers.<br />
 * The animation will be set only on layers with the same name and index.<br />
 * To set all animations on all layers, not checking their names or indices, use {@link DuAELayer.setAllAnims}.
 * @param {Layer[]|LayerCollection}	layers	- The layers.
 * @param {DuAELayerAnimation[]} anims	- The layer animations
 * @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 name animation.<br />
 * This way, only the type of property (i.e. matchName) is checked.
 * @param {Boolean}	[setExpression=false]	- Set the expression on the property
 * @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.
 * @param {Boolean}	[offset=false]	- true to offset the current value, instead of replacing it
 * @param {Boolean}	[reverse=false]	- true to reverse the keyframes (in time)<br />
 * Note: the remaining animations which are returned will already be reversed, do not set this to true again if you plan to set them later.
 * @return {DuAELayerAnimation[]} The animations which were not set (no corresponding layers)
 */
DuAELayer.setAnims = function(layers, anims, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, reverse) {
    //clone the array
    var remaining = anims.slice();

    if (reverse) DuAELayer.reverseAnims(anims);

    //for each layeranim, search for the layer and apply anim
    for (var i = remaining.length - 1; i >= 0; i--) {
        var anim = remaining[i];
        var it = new DuList(layers);
        var ok = false;
        while (layer = it.next()) {
            if (layer.name == anim._name && layer.index == anim._index) {
                DuAELayer.setAnim(layer, anim, time, ignoreName, setExpression, onlyKeyframes, replace, whiteList, offset, false);
                ok = true;
                break;
            }
        }
        if (ok) remaining.splice(i, 1);
    }

    return remaining;
}

/**
 * Reverses the times of the keyframes to reverse the animation
 * @param {DuAELayerAnimation[]|DuAELayerAnimation} anims The animation
 */
DuAELayer.reverseAnims = function(anims) {

    var it = new DuList(anims);

    //get times
    var startTime = null;
    var endTime = null;

    it.do(function(anim) {
        if (startTime == null && typeof anim.startTime !== 'undefined') startTime = anim.startTime;
        else if (startTime > anim.startTime) startTime = anim.startTime;
        if (endTime == null && typeof anim.endTime !== 'undefined') endTime = anim.endTime;
        else if (endTime < anim.endTime) endTime = anim.endTime;
    });

    var duration = endTime - startTime;

    //recursive function to reverse keyframes in anim
    function reverse(anim) {
        if (anim.type == 'anim') {
            //reverse the array
            anim.keys = anim.keys.reverse();
            //set the new times
            for (var i = 0, num = anim.keys.length; i < num; i++) {
                var ratio = 1 - ((anim.keys[i]._time - startTime) / duration);
                anim.keys[i]._time = ratio * duration + startTime;
                var sV = anim.endValue;
                anim.endValue = anim.startValue;
                anim.startValue = sV;
            }
        } else if (anim.anims) {
            for (var i = 0, num = anim.anims.length; i < num; i++) {
                reverse(anim.anims[i]);
            }
        }
    }

    //reverse keyframes and times
    it.do(reverse);
}

/**
 * Gets the children of a layer
 * @param {Layer}	layer	- The layer.
 * @return {Layer[]} All the children of the layer
 */
DuAELayer.getChildren = function(layer) {
    var comp = layer.containingComp;
    var children = [];
    for (var i = 1, numL = comp.layers.length; i <= numL; i++) {
        var l = comp.layer(i);
        if (l.index == layer.index) continue;
        if (l.parent == null) continue;
        if (l.parent.index == layer.index) children.push(l);
    }
    return children;
}

/**
 * Checks if a layer has at least one child.
 * @param {Layer} layer The layer to test
 * @returns {Boolean} true if the layer has at least one child.
 */
DuAELayer.hasChild = function(layer) {
    var comp = layer.containingComp;
    for (var i = 1, numL = comp.layers.length; i <= numL; i++) {
        var l = comp.layer(i);
        if (l.index == layer.index) continue;
        if (l.parent == null) continue;
        if (l.parent.index == layer.index) return true;
    }
    return false;
}

/**
 * Checks if a layer is a descendant of another layer
 * @param {Layer} layer1 - The first layer
 * @param {Layer} layer2 - The second layer
 * @return {int|null} the degree of relation. 0 if layer1 is not a relative of layer2,<br />
 * negative if layer2 is a descendant of layer1, positive if layer2 is an ancestor.<br />
 * null if the two layers are not in the same composition or if they are the same layer.
 */
DuAELayer.getRelation = function(layer1, layer2) {
    var comp1 = layer1.containingComp;
    var comp2 = layer2.containingComp;
    if (comp1 !== comp2) return null;
    if (layer1.index == layer2.index) return null;
    if (layer1.parent == null && layer2.parent == null) return 0;
    //check
    var degree = 0;
    var parent = layer1.parent;
    while (parent) {
        degree++;
        if (layer2.index == parent.index) return degree;
        parent = parent.parent;
    }
    var parent = layer2.parent;
    degree = 0;
    while (parent) {
        degree++;
        if (layer1.index == parent.index) return -degree;
        parent = parent.parent;
    }
    return 0;
}

/**
 * Measures the distance between two layers
 * @param {Layer} [layer1] - The first layer. If omitted, will use the selected layers in the current comp
 * @param {Layer} [layer2] - The second layer
 * @return {float} The distance (in pixels). -1 if less than two layers are found
 */
DuAELayer.getDistance = function(layer1, layer2) {

    if (!isdef( layer1 )) {
        var layers = DuAEComp.getSelectedLayers();
        if (layers.length < 2) return -1;
        layer1 = layers[0];
        layer2 = layers[1];
    }

    var O = DuAELayer.getWorldPos(layer1);
    var A = DuAELayer.getWorldPos(layer2);
    var OA = DuMath.length(O, A);

    return Math.round(OA);
}

/**
 * Gets the maximum distance between a bunch of layers
 * @param {Layer[]|DuList.<Layer>|LayerCollection} layers The layers
 * @return {float} The distance (in pixels)
 */
DuAELayer.getMaxDistance = function(layers) {
    layers = new DuList(layers);
    var numLayers = layers.length();
    var dist = 0;
    for (var i = 0; i < numLayers; i++) {
        for(var j = i+1; j < numLayers; j++) {
            var d = DuAELayer.getDistance(layers.at(i), layers.at(j));
            if (d > dist) dist = d;
        }
    }
    return dist;
}

/**
 * Gets the world coordinates of the point of a layer
 * @param {Layer} layer - The layer
 * @param {float[]} [point=layer.transform.anchorPoint.value] - the point
 * @param {float} [time] - the time at which to get the coordinates. Current time by default.
 * @return {float[]} The world coordinates of the layer
 */
DuAELayer.getWorldPos = function(layer, point, time) {
    time = def(time, layer.containingComp.time);

    if (layer instanceof CameraLayer || layer instanceof LightLayer || layer.threeDLayer) {
        var parent = layer.parent;
        layer.parent = null;
        var pos = layer.transform.position.valueAtTime(time, false);
        layer.parent = parent;
        return pos;
    }

    var matrix = DuAELayer.getTransformMatrix(layer, time);
    if (!isdef( point )) {
        if (layer.transform.anchorPoint) point = layer.transform.anchorPoint.valueAtTime(time, false);
    }

    return matrix.applyToPoint(point);
}

/**
 * Adds an animation preset on the layer.<br />
 * Be careful as layer selection will be kept but not properties selection,<br />
 * and this can result in an "invalid object" if referencing a property.
 * @param {Layer} layer - The layer
 * @param {File} preset - The preset file
 * @param {string} matchName - The pseudo Effect matchName
 * @return {PropertyGroup|null} The effect corresponding matchName or null if anything went wrong
 */
DuAELayer.applyPreset = function(layer, preset, matchName) {

    if (!isdef( layer )) { DuDebug.throwUndefinedError('layer', 'DuAELayer.applyPreset'); return null; }
    if (!isdef( preset )) { DuDebug.throwUndefinedError('preset', 'DuAELayer.applyPreset'); return null; }
    matchName = def(matchName, '');
    if (preset instanceof DuBinary) preset = preset.toFile();
    if (!(preset instanceof File)) preset = new File(preset);
    if (!preset.exists) throw new Error( i18n._("The pseudo effect file does not exist."));

    //remove layer selection
    var comp = layer.containingComp;
    var selection = DuAEComp.unselectLayers(comp);
    layer.selected = true;

    layer.applyPreset(preset);

    //applying a preset gets out of progress mode
    //if (DuAEProject.progressMode) DuAEProject.setProgressMode(true);

    var effect = null;
    if (layer.property("ADBE Effect Parade").numProperties > 0)
        effect = layer.property("ADBE Effect Parade")( layer.property("ADBE Effect Parade").numProperties );

    //restore selection
    DuAEComp.selectLayers(selection);

    return effect;
}

/**
 * This method is a workaround to AE API method layer.applyPreset to work like addProperty when adding pseudoEffects
 * @param {Layer} layer - The layer
 * @param {File} preset - The preset file
 * @param {string} matchName - The pseudo Effect matchName.
 * @param {string} [name] - The name to set on the effect
 * @return {PropertyGroup|null} The effect or null if anything went wrong
 */
DuAELayer.addPseudoEffect = function(layer, preset, matchName, name) {
    if (jstype(preset) != 'file') {
        DuDebug.throwTypeError(preset, 'preset', 'File', 'DuAELayer.addPseudoEffect');
        return null;
    }
    if (!preset.exists) {
        DuDebug.throwError( i18n._("The pseudo effect file does not exist."), 'DuAELayer.addPseudoEffect( layer, preset, matchName, name)');
        return null;
    }
    if (layer == undefined) return null;
    if (preset == undefined) return null;
    if (matchName == undefined) return null;

    var effects = layer("ADBE Effect Parade");
    //add the preset to a temp comp if not available as an effect
    if (!effects.canAddProperty(matchName) || DuESF.debug) {
        //create comp
        var comp = app.project.items.addComp("DuAEF Temp", 10, 10, 1, 1, 24);
        //add null
        var n = comp.layers.addNull();
        //apply preset
        n.applyPreset(preset);
        var fx = n.effect(1);
        if (fx) matchName = fx.matchName;
        //remove all
        var nullSource = n.source;
        n.remove();
        nullSource.remove();
        comp.remove();
    }

    if (!effects.canAddProperty(matchName)) {
        DuDebug.throwError( i18n._("Invalid pseudo effect file or match name."), 'DuAELayer.addPseudoEffect( layer, preset, matchName, name)');
        return null;
    }

    //add the pseudoEffect as a property
    var newEffectName = "";
    newEffectName = DuAELayer.newUniqueEffectName(name, layer);
    var effect = effects.addProperty(matchName);
    if (newEffectName != "") effect.name = newEffectName;
    return effect;
}

/**
 * Checks if the layers have some selected keyframes
 * @param {Layer[]|LayerCollection} layers - The layers
 * @return {boolean} true if the layers have at least one selected keyframe
 */
DuAELayer.haveSelectedKeys = function(layers) {
    var it = new DuList(layers);
    if (it.length() == 0) return;
    while (layer = it.next()) {
        var layerInfo = new DuAEProperty(layer);
        if (layerInfo.hasSelectedKeys()) return true;
    }
    return false;
}

/**
 * Gets the time of the first keyFrame
 * @param {Layer[]|LayerCollection} layer - The layer
 * @param {boolean} selected - true to check selected keyframes only
 * @return {float|null} The keyframe time or null if there are no keyframe
 */
DuAELayer.firstKeyFrameTime = function(layers, selected) {
    var it = new DuList(layers);
    var time = null;

    it.do(function(layer) {
        var layerProp = new DuAEProperty(layer);
        var test = layerProp.firstKeyTime(selected);
        if (time == null) time = test;
        else if (test != null) {
            if (time > test) time = test;
        }

    });

    return time;
}

/**
 * Sort the layers by their parenting (root at first index 0)
 * @param {Layer[]|Collection|DuList.<Layer>} layers - The layers to sort
 * @return {Layer[]} The sorted array
 */
DuAELayer.sortByParent = function(layers) {
    var sortedLayers = [];
    var layersToSort = new DuList(layers);

    //add layers with a parent outside
    //those with a parent outside of the selection
    for (var i = layersToSort.length() - 1; i >= 0; i--) {
        var l = layersToSort.at(i);
        var parent = l.parent;
        if (parent == null) continue;
        var isParentOutside = true;
        for (var j = 0, numL = layersToSort.length(); j < numL; j++) {
            if (parent.index == layersToSort.at(j).index) {
                isParentOutside = false;
                break;
            }
        }
        if (isParentOutside) {
            sortedLayers.push(l);
            layersToSort.remove(i);
        }
    }

    //add layers with no parents
    for (var i = layersToSort.length() - 1; i >= 0; i--) {
        var l = layersToSort.at(i);
        if (l.parent == null) {
            sortedLayers.push(l);
            layersToSort.remove(i);
        }
    }

    //sort the rest
    while (layersToSort.length() > 0) {
        for (var i = layersToSort.length() - 1; i >= 0; i--) {
            var l = layersToSort.at(i);
            for (var j = 0, numL = sortedLayers.length; j < numL; j++) {
                var sL = sortedLayers[j];
                if (l.parent.index == sL.index) {
                    sortedLayers.push(l);
                    layersToSort.remove(i);
                    break;
                }
            }
        }
    }
    return sortedLayers;

}

/**
 * Sort the layers by their indices. Returns a new Array, the original array or collection is not changed.
 * @param {Layer[]|LayerCollection|DuList.<Layer>} layers - The layers to sort
 * @return {Layer[]} The sorted array
 */
DuAELayer.sortByIndex = function(layers) {
    var sortedLayers = new DuList(layers);

    function compareLayerIndices(lay1, lay2) {
        return lay1.index - lay2.index;
    }

    return sortedLayers.sort(compareLayerIndices);
}

/**
 * Parents all the layers together beginning by the end of the array
 * @param {Layer[]|DuList.<Layer>} layers - The layers to parent
 */
DuAELayer.parentChain = function(layers) {
    layers = new DuList(layers);
    //unparent  all but the first
    var layersUnparent = [];
    for (var i = 1, numL = layers.length(); i < numL; i++) {
        layersUnparent.push(layers.at(i));
    }
    DuAELayer.unparent(layersUnparent);

    for (var i = layers.length() - 1; i >= 1; i--) {
        try {
            layers.at(i).parent = layers.at(i - 1);
        } catch (e) {
            if (DuESF.debug) alert(e);
        }
    }
}

/**
 * Un-parents all the layers
 * @param {Layer[]} layers - The layers
 */
DuAELayer.unparent = function(layers) {
    for (var i = layers.length - 1; i >= 0; i--) {
        layers[i].parent = null;
    }
}

/**
 * (Un)parent the children of the layer.< br/>
 * When children are unparented, an effect is added and the name of the layer is changed to show the "edit mode" is enabled.<br />
 * When toggled again, the effect is removed, and the name is restored.
 * @param {Layer} layer - The layer to toggle.
 */
DuAELayer.toggleEditMode = function(layer) {

    // Check if it's already in edit mode
    var setEditMode = true;
    var editMode = DuAETag.getValue(layer, DuAETag.Key.EDIT_MODE, DuAETag.Type.BOOL);
    if (editMode) {
        setEditMode = false;
    }

    var comp = layer.containingComp;

    // TRANSLATORS: must be relatively short
    var editName = i18n._p("in layer name", "Edit mode");

    if (setEditMode) {

        // Get children
        var children = DuAELayer.getChildren(layer);
        var childLayers = [];
        for (var j = 0, num = children.length; j < num; j++) {
            var child = children[j];
            childLayers.push(child.index);
            var l = child.locked;
            child.locked = false;
            child.parent = null;
            child.locked = l;
        }

        // Set the name
        layer.name = '=EDIT= ' + layer.name;
        // The previous tag name
        var tagName = DuAETag.getName(layer);
        // Set new Tag
        DuAETag.set(layer, tagName + ' | =' + editName + '=');
        // Add to edit group
        DuAETag.addGroup(layer, editName);
        // Store the list of children
        childLayers = childLayers.join(',');
        DuAETag.setValue(layer, DuAETag.Key.CHILD_LAYERS, childLayers);
        DuAETag.setValue(layer, DuAETag.Key.EDIT_MODE, true);

        // Tint the layer
        var tint = layer.property('ADBE Effect Parade').addProperty('ADBE Tint');
        tint.name = editName;
        tint(1).setValue([0.5, 0, 0, 1]);
        tint(2).setValue([1, 0, 0, 1]);

    } else {
        var childLayers = DuAETag.getValue(layer, DuAETag.Key.CHILD_LAYERS, DuAETag.Type.ARRAY);
        if (childLayers == null) return;
        for (var i = 0, n = childLayers.length; i < n; i++) {
            var index = parseInt(childLayers[i]);
            var child = comp.layer(index);
            var l = child.locked;
            child.locked = false;
            child.parent = layer;
            child.locked = l;
        }

        DuAETag.setValue(layer, DuAETag.Key.CHILD_LAYERS, '');
        DuAETag.setValue(layer, DuAETag.Key.EDIT_MODE, false);
        // Remove from edit group
        DuAETag.removeGroup(layer, editName);
        // Reset original tag
        var tagName = DuAETag.getName(layer);
        tagName = tagName.replace(' | =' + editName + '=', '');
        if (tagName == '') DuAETag.remove(layer);
        else DuAETag.set(layer, tagName)
        // Reset the layer name
        layer.name = layer.name.replace('=EDIT= ', '');

        //Remove tint
        layer.property('ADBE Effect Parade').property(editName).remove();
    }
}

/**
 * Creates a sequence with the layers, but using opacities.
 * This enables more possibilities to rig them, like with the Duik Connector
 * @param {Layer[]|LayerCollection} [layers] - The layers. The selected layers by default.
 * @param {string} [expr] - An expression to add to the opacity of the layers
 */
DuAELayer.sequence = function(layers, expr) {
    layers = def(DuAEComp.getSelectedLayers());
    if (layers.length == 0) return;
    expr = def(expr, '');
    var comp = layers[0].containingComp;
    var it = new DuList(layers);
    it.do(function(layer) {
        while (layer.transform.opacity.numKeys > 0) {
            layer.transform.opacity.removeKey(layer.transform.opacity.numKeys);
        }
        var i = it.current;
        var t = i * comp.frameDuration;
        var endTime = (layers.length - 1) * comp.frameDuration;
        layer.transform.opacity.setValueAtTime(0, 0);
        layer.transform.opacity.setValueAtTime(endTime, 0);
        layer.transform.opacity.setValueAtTime(t, 100);
        if (i < layers.length - 1) layer.transform.opacity.setValueAtTime(t + comp.frameDuration, 0);
        for (var keyIndex = 1; keyIndex <= layer.transform.opacity.numKeys; keyIndex++) {
            layer.transform.opacity.setInterpolationTypeAtKey(keyIndex, KeyframeInterpolationType.HOLD, KeyframeInterpolationType.HOLD);
        }
        layer.transform.opacity.expression = expr;
    });
}

/**
 * Adds a new Null object just above a layer, at the same position.<br />
 * This is a convenience function calling {@link DuAEComp.addNull}.
 * @param {Layer} layer - The layer
 * @return {Layer} the null
 */
DuAELayer.addNull = function(layer) {
    return DuAEComp.addNull(layer.containingComp, undefined, layer);
}

/**
 * Locks the scale with an expression so its value cannot be changed
 * @param {Layer} layer - The layer
 */
DuAELayer.lockScale = function(layer) {
    DuAEProperty.lock(layer.transform.scale);
}

/**
 * Copies the layers to another comp
 * @param {Layer[]} layers - The layers to copy and paste
 * @param {CompItem} destinationComp - The composition to copy to
 * @param {boolean} [withPropertyLinks=false] - Add expressions on the properties to link them to the orriginal layers<br />
 * Works only on 12.0 and above, ignored on 11.0 (CS6) and below
 * @return {Layer[]} The new layers
 */
DuAELayer.copyToComp = function(layers, destinationComp, withPropertyLinks) {
    if (DuAE.version.version < 12.0) withPropertyLinks = false;
    withPropertyLinks = def(withPropertyLinks, false);

    if (layers.length == 0) return;

    var it = new DuList(layers);

    var previousActiveComp = DuAEProject.getActiveComp();

    //activate the origin comp
    var originComp = it.first().containingComp;
    originComp.openInViewer();

    //select the layers
    var previousSelection = DuAEComp.unselectLayers(originComp);
    DuAEComp.selectLayers(layers);

    //copy
    if (withPropertyLinks) DuAE.copyWithPropertyLinks();
    else DuAE.copy();

    //destination
    destinationComp.openInViewer();

    //unselect layers in destination
    var previousSelectionDestination = DuAEComp.unselectLayers(destinationComp);

    //paste
    DuAE.paste();

    //keep new layers to return them
    var newLayers = destinationComp.selectedLayers;

    //restore previous state
    DuAEComp.selectLayers(previousSelection);
    previousActiveComp.openInViewer();
    DuAEComp.selectLayers(previousSelectionDestination);

    return newLayers;
}

/**
 * Parents all (unparented) layers
 * @param {Layer|LayerCollection|Layer[]|DuList.<Layer>} layers - The layers to parent
 * @param {Layer} [parent] - The parent. If not defined, will use the last layer of the list
 * @param {boolean} [unparentedOnly=true] - True to parent only layers which do not have a parent yet 
 * @param {boolean} [insert=false] - When true, the parent will be parented to the previous parent of the given layer (or first layer if the layers param is a list)
 */
DuAELayer.parent = function(layers, parent, unparentedOnly, insert) {
    unparentedOnly = def(unparentedOnly, true);
    insert = def(insert, false);
    layers = new DuList(layers);

    if (!isdef( parent )) {
        if (layers.length() <= 1) return;
        parent = layers.pop();
    }

    if (insert) parent.parent = layers.first().parent;

    layers.do(function(layer) {
        var locked = layer.locked;
        layer.locked = false;
        if ((layer.parent == null && unparentedOnly) || !unparentedOnly) {
            if (parent == null || layer.index != parent.index) layer.parent = parent;
        }
        layer.locked = locked;
    });
}

/**
 * Gets all the (selected) puppet pins found on the layer.<br />
 * Will return all puppet pins if there is no puppet selection.
 * @param {Layer} layer - The layer
 * @return {DuAEProperty[]} The properties
 */
DuAELayer.getPuppetPins = function(layer) {
    var pins = [];
    var selectedProps = layer.selectedProperties;

    function getPins(puppet) {
        //get pins
        var mesh = puppet.property("ADBE FreePin3 ARAP Group").property("ADBE FreePin3 Mesh Group").property("ADBE FreePin3 Mesh Atom").property("ADBE FreePin3 PosPins");
        for (var i = 1, num = mesh.numProperties; i <= num; i++) {
            pins.push(new DuAEProperty(mesh.property(i)));
        }
    }

    if (selectedProps == 0) {
        //look for puppet effects
        for (var i = 1, num = layer('ADBE Effect Parade').numProperties; i <= num; i++) {
            var effect = layer.effect(i);
            if (effect.matchName == 'ADBE FreePin3') {
                getPins(effect);
            }
        }
    } else {
        //get any selected pin
        var itProps = new DuList(selectedProps);
        itProps.do(function(prop) {
            if (prop.matchName == 'ADBE FreePin3 PosPin Atom') pins.push(new DuAEProperty(prop));
        });
        //try to find selected puppets
        if (pins.length == 0) {
            itProps.do(function(prop) {
                if (prop.matchName == 'ADBE FreePin3') getPins(prop);
            });
        }
    }


    return pins;
}

/**
 * Aligns a layer in position to another layer
 * @param {Layer} layer - The layer to align.
 * @param {Layer} target - The reference layer.
 */
DuAELayer.alignPosition = function(layer, target) {
    //parent to target
    var layerParent = layer.parent;
    if (layerParent != target) layer.parent = target;

    var comp = layer.containingComp;

    //TODO if dimensions dimensions separated
    //TODO if 3D

    //set position
    if (layer.transform.position.numKeys == 0) layer.transform.position.setValue(target.transform.anchorPoint.value);
    else layer.transform.position.setValueAtTime(comp.time, target.transform.anchorPoint.value);

    //reparent
    if (layerParent != target) layer.parent = layerParent;
}

/**
 * Aligns a layer's orientation to another layer
 * @param {Layer} layer - The layer to align.
 * @param {Layer} target - The reference layer.
 */
DuAELayer.alignOrientation = function(layer, target) {
    //parent to target
    var layerParent = layer.parent;
    if (layerParent != target) layer.parent = target;

    var comp = layer.containingComp;

    //TODO if 3D/2D

    //set rotation
    if (layer.transform.rotation.numKeys == 0) layer.transform.rotation.setValue(0);
    else layer.transform.rotation.setValueAtTime(comp.time, 0);

    //reparent
    if (layerParent != target) layer.parent = layerParent;
}

/**
 * Aligns a layer's scale to another layer
 * @param {Layer} layer - The layer to align.
 * @param {Layer} target - The reference layer.
 */
DuAELayer.alignScale = function(layer, target) {
    //parent to target
    var layerParent = layer.parent;
    if (layerParent != target) layer.parent = target;

    var comp = layer.containingComp;

    //TODO 3D

    //set scale
    if (layer.transform.scale.numKeys == 0) layer.transform.scale.setValue([100, 100]);
    else layer.transform.scale.setValueAtTime(comp.time, [100, 100]);

    //reparent
    if (layerParent != target) layer.parent = layerParent;
}

/**
 * Aligns a layer's opcaity to another layer
 * @param {Layer} layer - The layer to align.
 * @param {Layer} target - The reference layer.
 */
DuAELayer.alignOpacity = function(layer, target) {
    var comp = layer.containingComp;

    //set scale
    if (layer.transform.opacity.numKeys == 0) layer.transform.opacity.setValue(target.transform.opacity.value);
    else layer.transform.opacity.setValueAtTime(comp.time, target.transform.opacity.value);
}

/**
 * Aligns the layers' transformations (position, rotation, scale) to another layer
 * @param {Layer[]|LayerCollection} layers - The layers to align.
 * @param {Layer} target - The reference layer.
 * @param {boolean} [position=true] - True to align position.
 * @param {boolean} [rotation=true] - True to align orientation.
 * @param {boolean} [scale=true] - True to align scale.
 * @param {boolean} [opacity=false] - True to align opcacity.
 */
DuAELayer.align = function(layers, target, position, rotation, scale, opacity) {
    position = def(position, true);
    rotation = def(rotation, true);
    scale = def(scale, true);
    opacity = def(opacity, false);

    var it = new DuList(layers);

    var targetParent = target.parent;
    target.parent = null;

    //set values
    it.do(function(layer) {
        if (layer == target) return;

        if (position) DuAELayer.alignPosition(layer, target);
        if (rotation) DuAELayer.alignOrientation(layer, target);
        if (scale) DuAELayer.alignScale(layer, target);
        if (opacity) DuAELayer.alignOpacity(layer, target);
    });

    target.parent = targetParent;
}

/**
 * Gets the transformation matrix of the layer from the compostion.<br />
 * Use Matrix.applyToPoint(point) to transform any coordinate with the matrix returned by this method.
 * @param {Layer[]} layer - the layer
 * @param {float} [time] - the time at which to get the coordinates. Current time by default.
 * @return {Matrix} The coordinates.
 */
DuAELayer.getTransformMatrix = function(layer, time) {

    time = def(time, layer.containingComp.time);

    var matrix = new Matrix();

    //get the ancestors
    var layers = [layer];
    var parent = layer.parent;
    while (parent) {
        layers.push(parent);
        parent = parent.parent;
    }

    //apply transforms from the ancestor
    for (var i = layers.length - 1; i >= 0; i--) {
        var l = layers[i];

        //position
        matrix.translate(l.transform.position.valueAtTime(time, false));

        //rotation
        if (l.threeDLayer || l instanceof CameraLayer) matrix.rotate(l.transform.zRotation.valueAtTime(time, false));
        else matrix.rotate(l.transform.rotation.valueAtTime(time, false));

        if (!(l instanceof CameraLayer)) {
            //anchor point inverse transform, taking scale into account
            var ap = l.transform.anchorPoint.valueAtTime(time, false);
            var sca = l.transform.scale.valueAtTime(time, false);
            var aPX = -(ap[0] * sca[0] / 100);
            var aPY = -(ap[1] * sca[1] / 100);

            matrix.translate([aPX, aPY]);

            //scale
            matrix.scale(l.transform.scale.valueAtTime(time, false) / 100);
        }
    }

    return matrix;
}

/**
 * Moves a layer to the coordinates of a spatial property
 * @param {Layer} layer - The layer
 * @param {Property|DuAEProperty} prop - The property
 */
DuAELayer.moveLayerToProperty = function(layer, prop) {
    var propInfo;
    if (prop instanceof DuAEProperty) {
        propInfo = prop;
        prop = propInfo.getProperty();
    } else {
        propInfo = new DuAEProperty(prop);
    }

    var propLayer = propInfo.layer;
    var comp = propInfo.comp;

    if (!(propLayer instanceof ShapeLayer)) {
        var matrix = DuAELayer.getTransformMatrix(propLayer);
        var pos = matrix.applyToPoint(prop.value);
        var parent = layer.parent;
        layer.parent = null;
        layer.position.setValue(pos);
        layer.parent = parent;
    } else {
        var parent = layer.parent;
        layer.parent = null;
        layer.position.setValue(prop.value);
        layer.parent = parent;
    }
}

/**
 * Sets the In and Out points of a layer according to its opacity (cuts at 0%)
 * @param {Layer} layer - The layer
 * @param {boolean} [preExpression=false] - Whether to check for the opacity post or pre-expression value
 */
DuAELayer.autoDuration = function(layer, preExpression) {
    if (!isdef( preExpression )) preExpression = false;

    var comp = layer.containingComp;

    var inPoint = layer.inPoint;
    var outPoint = layer.outPoint;
    var inFrame = inPoint / comp.frameDuration;
    var outFrame = outPoint / comp.frameDuration;

    //search in
    if (layer.transform.opacity.valueAtTime(inPoint, preExpression) == 0) {
        for (var i = inFrame; i < outFrame; i++) {
            var time = i * comp.frameDuration;
            if (layer.transform.opacity.valueAtTime(time, preExpression) == 0) inPoint = time + comp.frameDuration;
            else break;
        }
    }

    //search out 
    if (layer.transform.opacity.valueAtTime(outPoint, preExpression) == 0) {
        for (var i = outFrame; i > inFrame; i--) {
            var time = i * comp.frameDuration;
            if (layer.transform.opacity.valueAtTime(time, preExpression) == 0) outPoint = time;
            else break;
        }
    }

    //set new in and out points
    if (inPoint != layer.inPoint) layer.inPoint = inPoint;
    if (outPoint != layer.outPoint) layer.outPoint = outPoint;
}

/**
 * Checks if a layer is 3D (ie is a threeDLayer or a camera or a light)
 * @param {Layer} layer - The layer
 * @return {bool} true if the layer is a 3D layer
 */
DuAELayer.isThreeD = function(layer) {
    if (layer.threeDLayer) return true;
    if (layer instanceof CameraLayer) return true;
    if (layer instanceof LightLayer) return true;
    return false;
}

/**
 * Gets an expression linking to the layer
 * @param {Layer} layer The layer
 * @param {Boolean} [useThisComp=false] Whether to begin the expression by 'thisComp' or 'comp("name")'
 * @return {str} The expression link to the layer
 */
DuAELayer.expressionLink = function(layer, useThisComp) {
    useThisComp = def(useThisComp, false);

    var comp = layer.containingComp;
    var exprCode;
    // Prefix the layer reference
    name = "\"" + layer.name + "\"";
    exprCode = "layer(" + name + ")";
    // Prefix the comp reference
    if (useThisComp) exprCode = "thisComp." + exprCode;
    else exprCode = "comp(\"" + comp.name + "\")." + exprCode;

    return exprCode;
}

/**
 * Checks if the given layer is a solid.
 * @param {Layer} layer The layer to test
 * @returns {Boolean} true if it is a solid
 */
DuAELayer.isSolid = function(layer) {
    if (layer.nullLayer) return false;

    if (DuAELayer.isType(layer, DuAELayer.Type.SOLID)) return true;

    if (!(layer instanceof AVLayer)) return false;

    if (!(layer.source instanceof FootageItem)) return false;

    if (layer.source.mainSource instanceof SolidSource) return true;
}

/**
 * Checks if the given layer is a precomposition.
 * @param {Layer} layer The layer to test
 * @returns {Boolean} true if it is a composition
 */
DuAELayer.isComp = function(layer) {
    if (layer.nullLayer) return false;

    if (!(layer instanceof AVLayer)) return false;

    if (layer.source instanceof CompItem) return true;

    return false;
}

/**
 * Checks if the layer is inside the bounds of the composition
 * @param {Layer} layer the layer to check
 * @param {Boolean} [useBounds=false] (not implemented yet) Checks the layer bounds if true, just the anchor point if false.
 * @returns {Boolean} true if the layer is inside the composition
 */
DuAELayer.insideComp = function(layer, useBounds) {
    useBounds = def(useBounds, false);

    var comp = layer.containingComp;
    var layerPos = DuAELayer.getWorldPos(layer);

    var compBounds = [0, comp.width, 0, comp.height, -9999999, 9999999];
    return DuMath.isInside(layerPos, compBounds);
}

/**
 * Moves a layer in the center of the comp if it is outside
 * @param {Layer} layer the layer to check
 * @param {Boolean} [useBounds=false] (not implemented yet) Checks the layer bounds if true, just the anchor point if false.
 */
DuAELayer.moveInsideComp = function(layer, useBounds) {
    if (DuAELayer.insideComp(layer)) return;
    var comp = layer.containingComp;
    var layerParent = layer.parent;
    layer.parent = null;
    layer.transform.position.setValue([comp.width / 2, comp.height / 2]);
    layer.parent = layerParent;
}

/**
 * Gets the last corresponding effect (instead of the first with the native layer.effect() method)
 * @param {Layer} layer The layer
 * @param {string} [name] The name or matchname to look for. If omitted, will return the last effect.
 * @param {int} [skip=0] Number of effects to skip
 * @returns {PropertyGroup|null} The effect or null if not found.
 */
DuAELayer.lastEffect = function(layer, name, skip) {
    skip = def(skip, 0);
    name = def(name, '');

    if (layer.property('ADBE Effect Parade').numProperties <= skip) return null;

    for (var i = layer.property('ADBE Effect Parade').numProperties - skip, n = 0; i > n; i--) {
        var e = layer.effect(i);
        if (e.matchName == name) return e;
        if (e.name == name) return e;
    }
    return null;
}

/**
 * Changes the coordinates of the anchor point without moving the layer
 * @param {Layer} layer The layer
 * @param {float[]} value The new coordinates
 */
DuAELayer.repositionAnchorPoint = function(layer, value) {
    // Unparent
    var parent = layer.parent;
    layer.parent = null;

    // Get future position for each position keyframe

    // New positions, one per keyframe
    var newPositions = [];
    var newXPositions = [];
    var newYPositions = [];
    var newZPositions = [];
    // The position property
    if (layer.transform.position.dimensionsSeparated) {
        var p = layer.transform.property('ADBE Position_0');
        var nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++) {
                var newPos = DuAELayer.getWorldPos(layer, value, p.keyTime(i));
                newXPositions.push(newPos[0]);
            }
        } else {
            var newPos = DuAELayer.getWorldPos(layer, value);
            newXPositions.push(newPos[0]);
        }

        p = layer.transform.property('ADBE Position_1');
        nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++) {
                var newPos = DuAELayer.getWorldPos(layer, value, p.keyTime(i));
                newYPositions.push(newPos[1]);
            }
        } else {
            var newPos = DuAELayer.getWorldPos(layer, value);
            newYPositions.push(newPos[1]);
        }

        p = layer.transform.property('ADBE Position_2');
        nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++) {
                var newPos = DuAELayer.getWorldPos(layer, value, p.keyTime(i));
                newZPositions.push(newPos[2]);
            }
        } else {
            var newPos = DuAELayer.getWorldPos(layer, value);
            newZPositions.push(newPos[2]);
        }
    }
    else {
        // Number of keyframes
        var p = layer.transform.position;
        var nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++) {
                var newPos = DuAELayer.getWorldPos(layer, value, p.keyTime(i));
                newPositions.push(newPos);
            }
        } else {
            var newPos = DuAELayer.getWorldPos(layer, value);
            newPositions.push(newPos);
        }
    }
    

    // Set anchor point
    layer.transform.anchorPoint.setValue(value);

    // Adjust position for keyframes
    if (layer.transform.position.dimensionsSeparated) {
        var p = layer.transform.property('ADBE Position_0');
        var nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++)
                p.setValueAtKey(i, newXPositions[i - 1]);
        }
        else p.setValue(newXPositions[0]);
        
        p = layer.transform.property('ADBE Position_1');
        nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++)
                p.setValueAtKey(i, newYPositions[i - 1]);
        }
        else p.setValue(newYPositions[0]);

        p = layer.transform.property('ADBE Position_2');
        nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++)
                p.setValueAtKey(i, newZPositions[i - 1]);
        }
        else if (layer.threeDLayer) p.setValue(newZPositions[0]);
    }
    else {
        var p = layer.transform.position;
        nK = p.numKeys;
        if (nK > 0) {
            for (var i = 1; i <= nK; i++)
                p.setValueAtKey(i, newPositions[i - 1]);
        }
        else p.setValue(newPositions[0]);
    }
    
    // Reparent
    layer.parent = parent;
}

/**
 * Returns the bounds of the layer in local coordinates, like the sourceRectAtTime() function does in expressions, but can also include masks.
 * @param {Layer} layer The layer
 * @param {float} [time] The time at which to get the bounds, the current time by default
 * @param {Boolean} [includeExtents=true] Includes the extents (strokes, accents...)
 * @param {Boolean} [includeMasks=true] Includes the masks
 * @return {float[]} The bounds [top, left, width, height]
 */
DuAELayer.sourceRect = function(layer, time, includeExtents, includeMasks) {
    time = def(layer.containingComp.time);
    includeExtents = def(includeExtents, true);
    includeMasks = def(includeMasks, true);

    var top = 0;
    var left = 0;
    var width = layer.width;
    var height = layer.height;

    // Texts and shapes: use an expression
    if (layer instanceof ShapeLayer || layer instanceof TextLayer) {
        // Expression
        var exp = 'thisLayer.sourceRectAtTime(' + time + ',';
        if (includeExtents) exp += 'true';
        else exp += 'false';
        exp += ').{*};'

        // Create a slider
        var slider = layer.property('ADBE Effect Parade').addProperty('ADBE Slider Control');

        slider(1).expression = exp.replace('{*}', 'top');
        top = slider(1).value;
        slider(1).expression = exp.replace('{*}', 'left');
        left = slider(1).value;
        slider(1).expression = exp.replace('{*}', 'width');
        width = slider(1).value;
        slider(1).expression = exp.replace('{*}', 'height');
        height = slider(1).value;

        // Remove slider
        slider.remove();
    }

    // Get masks
    if (includeMasks) {
        var masks = DuAEProperty.getProps(layer.property('ADBE Mask Parade'), 'ADBE Mask Shape');
        if (masks.length > 0) {
            var maskBounds = DuAEProperty.pathBounds(masks, false);
            if (maskBounds[0] > top) top = maskBounds[0];
            if (maskBounds[1] > left) left = maskBounds[1];
            if (maskBounds[2] < width) width = maskBounds[2];
            if (maskBounds[3] < height) height = maskBounds[3];
        }

    }

    return [top, left, width, height];
}

/**
 * Gets or create a layer control effect targetting the given target layer.
 * @param {Layer} layer The layer to get the effect from
 * @param {Layer} targetLayer The targetted layer, which must be in the same comp than the layer.
 * @param {string} [effectName] The name to use when creating the effect.
 * @return {DuAEProperty|null} The effect or null if the two layers are not in the same comp.
 */
DuAELayer.getCreateLayerEffect = function (layer, targetLayer, effectName) {
    var sourceComp = layer.containingComp;
    var targetComp = targetLayer.containingComp;
    if (sourceComp !== targetComp) return null;
    var sourceEffects = layer.property('ADBE Effect Parade');
    for (var i = 1, n = sourceEffects.numProperties; i <= n; i++)
    {
        var fx = sourceEffects(i);
        if (fx.matchName != 'ADBE Layer Control') continue;
        if (fx(1).value == targetLayer.index) return fx;
    }
    var fx = sourceEffects.addProperty('ADBE Layer Control');
    if (isdef( effectName )) fx.name = DuAELayer.newUniqueEffectName(effectName, layer);
    fx(1).setValue(targetLayer.index);
    return fx;
}

/**
 * Creates a new locator linked to the layer
 * @param {Layer|CompItem} [layerOrComp] The layer or the containing comp
 * @returns {ShapeLayer} The locator
 */
DuAELayer.createLocator = function( layerOrComp ) {
    var layer = null;
    var comp = null;
    if (!isdef( layerOrComp )) comp = DuAEProject.getActiveComp();
    else if (layerOrComp instanceof CompItem) comp = layerOrComp;
    else {
        layer = layerOrComp;
        comp = layer.containingComp;
    }
    if (!comp) return null;

    //create null object
    var loc = DuAEComp.addNull(comp, 50);
    loc.moveToEnd();
    var name = 'LOC';
    if (layer) Duik.Layer.copyAttributes(loc, layer, Duik.Layer.Type.LOCATOR);
    else Duik.Layer.setAttributes(loc, Duik.Layer.Type.LOCATOR, i18n._("Locator"));

    //add layer control
    var layerfx = loc.property('ADBE Effect Parade').addProperty('ADBE Layer Control');
    layerfx.name = i18n._("Locator");
    if (layer) layerfx(1).setValue(layer.index);

    //add expressions
    loc.transform.rotation.expression = [DuAEExpression.Id.LOCATOR,
        DuAEExpression.Library.get(['sign', 'dishineritRotation', 'getOrientation']),
        'var result = 0;',
        'var l = null;',
        'try{ l = effect("' + layerfx.name + '")(1) } catch(e) { }',
        'if (l)',
        '{',
        '    var sign = getScaleMirror( thisLayer );',
        '    var flip = getScaleUTurn( thisLayer );',
        '    result = getOrientation( l ) * sign + flip;',
        '    result += dishineritRotation( thisLayer ) - value;',
        '}',
        'result;'
    ].join('\n');

    loc.transform.position.expression = [DuAEExpression.Id.LOCATOR,
        'var result = [thisComp.width/2,thisComp.height/2];',
        'var l = null;',
        'try{ l = effect("' + layerfx.name + '")(1) } catch(e) { }',
        'if (l)',
        '{',
        '   result = l.toWorld(l.anchorPoint);',
        '}',
        'if (hasParent)',
        '{',
        '   result = parent.fromWorld(result);',
        '}',
        'result;'
    ].join('\n');

    loc.transform.anchorPoint.expression = [DuAEExpression.Id.LOCATOR,
        'var result = value;',
        'var l = null;',
        'try{ l = effect("' + layerfx.name + '")(1) } catch(e) { }',
        'if (l)',
        '{',
        '   result =l.anchorPoint.value;',
        '}',
        'result;'
    ].join('\n');

    loc.transform.scale.expression = [DuAEExpression.Id.LOCATOR,
        'var l = null;',
        'try{ l = effect("' + layerfx.name + '")(1) } catch(e) { }',
        DuAEExpression.Library.get(['getScale', 'dishineritScale']),
        '',
        'var threeD = value.length == 3;',
        'var result = dishineritScale( thisLayer ) - value;',
        'if (threeD) result += [100,100,100];',
        'else result += [100,100];',
        '',
        'if (l)',
        '{',
        '    var sl = getScale( l )/100;',
        '    if (result.length == 3 && sl.length == 3) {',
        '        result = [ result[0]*sl[0], result[1]*sl[1], result[2]*sl[2] ];',
        '    }',
        '    else if (result.length == 3) {',
        '        result = [result[0]*sl[0], result[1]*sl[1], result[2] ];',
        '    }',
        '    else {',
        '        result = [result[0]*sl[0], result[1]*sl[1] ];',
        '    }',
        '}',
        '',
        'result;'
    ].join('\n');

    return loc;
}

/**
 * Checks if a layer is renderable. A non-renderable layer can be:<br />
 * <ul>
 * <li>A Null layer</li>
 * <li>A Guide layer</li>
 * <li>An empty shape layer</li>
 * <li>An empty text layer</li>
 * <li>A layer with the opacity at 0% for the whole composition</li>
 * </ul>
 * @return {Bool}
 */
DuAELayer.isRenderable = function(layer) {
    if (!layer.enabled) return false;
    if (layer.guideLayer) return false;
    if (layer.nullLayer) return false;
    if (layer instanceof ShapeLayer) {
        var contents = layer.property("ADBE Root Vectors Group");
        if (contents.numProperties == 0) return false;
    }
    if (layer instanceof TextLayer) {
        var contents = layer.sourceText.value.text;
        if (contents == "") return false;
    }
    if (layer.transform.opacity.expression == "" && layer.transform.opacity.value == 0) return false;
    else if (layer.transform.opacity.expression == "") return true;

    // Check opacity for each frame if there's an expression in it
    var comp = layer.containingComp;
    var frameDuration = comp.frameDuration;
    for (var i = 0, n = comp.duration; i <= n; i += frameDuration) {
        if (layer.transform.opacity.valueAtTime(i, false) != 0) return true;
    }

    return false;
}

/**
 * Bakes the expressions to keyframes and removes all non-renderable layers.
 * @param {Layer} layer The layer to bake.
 * @param {DuAEExpression.BakeAlgorithm} [mode=DuAEExpression.BakeAlgorithm.SMART] The algorithm to use for baking the expressions.
 * @param {float} [frameStep=1.0] By default, checks one value per keyframe. A lower value increases the precision and allows for sub-frame sampling. A higher value is faster but less precise.
 */
DuAELayer.bake = function (mode, frameStep, layer) {
    // First, check transform properties.
    // If the parent layer is not renderable, we need a locator to get the transform properties
    var parent = layer.parent;
    var locked = layer.locked;
    layer.locked = false;
    if (parent) {
        if (!DuAELayer.isRenderable(parent)) {
            // Add locator
            var loc = DuAELayer.createLocator(layer);
            // Bake it's transform properties
            var transformProp = new DuAEProperty( loc.transform );
            transformProp.bakeExpressions( mode, frameStep );
            // unparent the layer
            layer.parent = null;

            // Copy the locator transform property keyframes to the layer transform properties
            loc.transform.anchorPoint.expression = "";
            var apAnim = new DuAEProperty( loc.transform.anchorPoint ).animation();
            layer.transform.anchorPoint.expression = "";
            var ap = new DuAEProperty( layer.transform.anchorPoint );
            ap.setAnim( apAnim, 0, false, true);

            loc.transform.position.expression = "";
            var posAnim = new DuAEProperty( loc.transform.position ).animation();
            layer.transform.position.expression = "";
            var pos = new DuAEProperty( layer.transform.position );
            pos.setAnim( posAnim, 0, false, true);

            loc.transform.rotation.expression = "";
            var rotAnim = new DuAEProperty( loc.transform.rotation ).animation();
            layer.transform.rotation.expression = "";
            var rot = new DuAEProperty( layer.transform.rotation );
            rot.setAnim( rotAnim, 0, false, true);

            loc.transform.scale.expression = "";
            var scaAnim = new DuAEProperty( loc.transform.scale ).animation();
            layer.transform.scale.expression = "";
            var sca = new DuAEProperty( layer.transform.scale );
            sca.setAnim( scaAnim, 0, false, true);

            // Remove the locator
            loc.remove();
        }
    }

    // Bake all expressions on the layer
    new DuAEProperty( layer ).bakeExpressions( mode, frameStep );

    layer.locked = locked;
}

/**
 * Gets the actual width of a layer (including it's scale)
 * @param {AVLayer} layer The layer
 * @return {Number} The width, in pixels.
 */
DuAELayer.width = function( layer ) {

    var parent = layer.parent;
    layer.parent = null;

    var rect = layer.sourceRectAtTime(0,false);
    var w = rect.width*layer.transform.scale.value[0]/100;
    w = Math.abs(w);

    layer.parent = parent;

    return w;
}

/**
 * Gets the actual height of a layer (including it's scale)
 * @param {AVLayer} layer The layer
 * @return {Number} The height, in pixels.
 */
DuAELayer.height = function( layer ) {

    var parent = layer.parent;
    layer.parent = null;

    var rect = layer.sourceRectAtTime(0,false);
    var h = rect.height*layer.transform.scale.value[0]/100;
    h = Math.abs(h);

    layer.parent = parent;

    return h;
}

/**
 * Stacks the layers in the timeline according to their order in the given array/DuList
 * @param {LayerCollection|Layer[]|DuList.<Layer>} layers The layers to stack
 */
DuAELayer.stack = function( layers ) {
    layers = new DuList(layers);
    var l = layers.next();
    while(l = layers.next()) {
        l.moveBefore(layers.at( layers.current - 1));
    }
}

/**
 * Sets the new coordinates of the layer to translate it by offset.
 * @param {Layer} layer The layer to move
 * @param {float[]} offset The value of the translation. A two or three dimensionnal array.
 * @param {boolean} [world=false] Set to true to offset in world coordinates.
 */
DuAELayer.translate = function( layer, offset, world ){

    var p = layer.transform.position.value;
    p[0] += offset[0];
    p[1] += offset[1];
    if (offset.length == 3) {
        if (p.length == 2) p.push( offset [2] );
        else p[2] += offset[2];
    }

    DuAELayer.setPosition( layer, p, world );
}

/**
 * Sets the new coordinates of the layer.
 * @param {Layer} layer The layer to move 
 * @param {float[]} position The new coordinates
 * @param {boolean} [world=false] Set to true to use world coordinates.
 */
DuAELayer.setPosition = function( layer, position, world ) {
    world = def(world, false);

    var l = layer.locked;
    layer.locked = false;

    var p = layer.parent;
    if (world) layer.parent = null;

    var pos = new DuAEProperty( layer.transform.position );
    pos.setValue(position);

    if (world) layer.parent = p;
    layer.locked = l;
}

/**
 * Checks if the layer has some keyframes
 * @param {Layer} layer The layer
 * @return {Boolean}
 */
DuAELayer.hasKeys = function (layer) {
    var p = new DuAEProperty(layer);
    return p.hasKeys();
}

/**
 * Checks if the layer has some expressions
 * @param {Layer} layer The layer
 * @return {Boolean}
 */
DuAELayer.hasExpressions = function (layer) {
    var p = new DuAEProperty(layer);
    return p.hasExpressions();
}

/**
 * Checks if the layer has some masks
 * @param {Layer} layer The layer
 * @return {Boolean}
 */
DuAELayer.hasMask = function (layer) {
    return layer.property("ADBE Mask Parade").numProperties > 0;
}

/**
 * Checks if this is a null layer, either a true AE Null layer,
 * or a "Shape as Null" as created by DuAEF with {@link DuAEComp.addNull}.
 * @param {Layer} layer The layer to test
 * @return {bool}
 */
DuAELayer.isNull = function( layer ) {
    if (layer.nullLayer) return true;

    return DuAELayer.isType( layer, DuAELayer.Type.NULL );
}

DuAELayer.isAdjustment = function ( layer ) {
    if (layer.adjustmentLayer) return true;

    return DuAELayer.isType( layer, DuAELayer.Type.ADJUSTMENT );
}// ==================== |--------| ====================
// ==================== | puppet | ====================
// ==================== |--------| ====================


/**
 * Puppet tool methods
 * @namespace
 * @category DuAEF
 */
var DuAEPuppet = {};

/**
 * Checks if a pin can be rigged or not.\nFor now, the only pins which can not be rigged are starch pins.
 * @param {PropertyGroup} pin - The pin to test
 * @return {boolean} True if this pin has either a position, rotation or scale property
 */
DuAEPuppet.riggable = function ( pin ) {
    if ( pin.position.canSetExpression ) return true;
    if ( pin.rotation.canSetExpression ) return true;
    if ( pin.scale.canSetExpression ) return true;
    return false;
}// ==================== |-------------| ====================
// ==================== | renderqueue | ====================
// ==================== |-------------| ====================

/**
 * Render Queue methods
 * @namespace
 * @category DuAEF
 */
var DuAERenderQueue = {};

/**
 * Checks if the given template is installed
 * @param {string} templateName The name of the template
 * @return {Boolean} true if the template is available
 */
DuAERenderQueue.hasRenderSettingsTemplate = function (templateName)
{
    // create fake comp to get templates
    var tempComp = app.project.items.addComp("DuAEF Temp", 4, 4, 1, 1, 24);
    var tempItem = app.project.renderQueue.items.add(tempComp);
    var itemTemplates = tempItem.templates;
    for (var i = 0, num = itemTemplates.length; i < num; i++)
    {
        if ( itemTemplates[i] == templateName )
        {
            tempComp.remove();
            return true;
        };
    }
    tempComp.remove();
    return false;
}

/**
 * Checks if the given template is installed
 * @param {string} templateName The name of the template
 * @return {Boolean} true if the template is available
 */
DuAERenderQueue.hasOutputModuleTemplate = function (templateName)
{
    // create fake comp to get templates
    var tempComp = app.project.items.addComp("DuAEF Temp", 4, 4, 1, 1, 24);
    var tempItem = app.project.renderQueue.items.add(tempComp);
    var omTemplates = tempItem.outputModule(1).templates;
    for (var i = 0, num = omTemplates.length; i < num; i++)
    {
        if ( omTemplates[i] == templateName )
        {
            tempComp.remove();
            return true;
        };
    }
    tempComp.remove();
    return false;
}// ==================== |------------| ====================
// ==================== | shapelayer | ====================
// ==================== |------------| ====================

/**
 * Shape Layer methods
 * @namespace
 * @category DuAEF
 */
var DuAEShapeLayer = {};

/**
 * A List of primitive shapes
 * @enum {int}
 */
DuAEShapeLayer.Primitive = {
    NONE: 0,
    CIRCLE: 1,
    SQUARE: 2,
    ROUNDED_SQUARE: 3,
    POLYGON: 4,
    STAR: 5
}

/**
 * Gets the transformation matrix for all the parent groups of a given property
 * @param {Property|DuAEProperty} prop - The property
 * @return {Matrix} The transformation matrix.
 */
DuAEShapeLayer.getTransformMatrix = function ( prop ) {
    var propInfo = new DuAEProperty( prop );
    prop = propInfo.getProperty();
    var matrix = DuAELayer.getTransformMatrix( propInfo.layer );

    var props = [];

    //get the groups
    var parentProp = prop;
    while ( parentProp.parentProperty ) {
        if ( parentProp.matchName == "ADBE Vector Group" ) {
            props.push( parentProp );
        }
        // Traverse up the property tree
        parentProp = parentProp.parentProperty;
    }

    //apply transforms from the ancestor
    for ( var i = props.length - 1; i >= 0; i-- ) {
        var p = props[ i ].property( "ADBE Vector Transform Group" );
        matrix.translate( -p.property( "ADBE Vector Anchor" ).value );
        matrix.translate( p.property( "ADBE Vector Position" ).value );
        matrix.rotate( p.property( "ADBE Vector Rotation" ).value );
        matrix.scale( p.property( "ADBE Vector Scale" ).value / 100 );
    }

    return matrix;
}

/**
 * Checks if this shape layers contains only one shape, one fill, and one stroke,\n
 * just like After Effects creates them at first.
 * @param {Layer} layer - The layer to test
 * @return {bool} True if the layer is a shape layer containing only one shape, one fill, and one stroke in a group. False otherwise.
 */
DuAEShapeLayer.isSingleShape = function ( layer ) {
    if ( !( layer instanceof ShapeLayer ) ) return false;

    var content = layer.property( "ADBE Root Vectors Group" );
    if ( content.numProperties != 1 ) return false;

    var shape = content.property( "ADBE Vector Group" ).property( "ADBE Vectors Group" );
    if ( shape.numProperties != 3 ) return false;

    var path = shape.property( 1 );
    if ( path.matchName != "ADBE Vector Shape - Group" && path.matchName != "ADBE Vector Shape - Rect" && path.matchName != "ADBE Vector Shape - Ellipse" && path.matchName != "ADBE Vector Shape - Star" ) return false;
    var stroke = shape.property( 2 );
    if ( stroke.matchName != "ADBE Vector Graphic - Stroke" ) return false;
    var fill = shape.property( 3 );
    if ( fill.matchName != "ADBE Vector Graphic - Fill" ) return false;

    return true;
}
// ==================== |--------------| ====================
// ==================== | pseudoEffect | ====================
// ==================== |--------------| ====================

/**
	* Constructs a Pseudo Effect.
	* @class DuAEPseudoEffect
	* @classdesc Describes a pseudo effect
	* @param {DuBinary} binaryFile - The ffx file encoded as a {@link DuBinary}.<br />
	* Note that the pseudo effect's matchName <strong>must start with <code>"Pseudo/"</code></strong>.
	* @category DuAEF
    */
function DuAEPseudoEffect( binaryFile )
{
	/**
	 * Will be false if the ffx can't be correctly parsed.<br />
	 * Note that an invalid pseudo effect can still be applied (if the file exists), but the new effect may not be returned,<br />
	 * or the {@link DuAEPseudoEffect.index} Object may be empty.
	 * @type {Boolean}
	 * @name valid
	 * @memberof DuAEPseudoEffect
	 * @readonly
	 */
	this.valid = true;

	// Get the matchname

	/**
	 * The matchName
	 * @type {string}
	 * @name matchName
	 * @memberof DuAEPseudoEffect
	 * @readonly
	 */
	this.matchName = '';
	// A RegEx to get the matchName
	var reMatchName = /\((Pseudo\/[a-z\s0-9.]+)-/gi;

	var matchMatchName = reMatchName.exec( binaryFile.binAsString );
	if (matchMatchName)
	{
		this.matchName = matchMatchName[1];
	}

	if (this.matchName == '') this.valid = false;

	// Get other infos

	/**
	 * The default (localized) name
	 * @type {string}
	 * @name name
	 * @memberof DuAEPseudoEffect
	 * @readonly
	 */
	this.name = '';

	// We need to get the JSON descriptive array
	// It can be stored either in a JSON object, or in an XML tag
	var reJSON = /{.*\"controlName\"\s*:\s*\"([^\n\r\"]+)\".*/gi;
	var reXML = /<\s*control name\s*=\s*\"([^\n\r\"]+)\"\s*>(.*)<\s*\/\s*control\s*>/gi


	var matchArray = reJSON.exec( binaryFile.binAsString );
	var indexArray = [];
	if (matchArray)
	{
		// For some reason, the leading { may not be captured...
		if (!matchArray[0].indexOf("{") == 0) matchArray[0] = "{" + matchArray[0];
		// Parse JSON and get the prop array
		var jsonObj = JSON.parse( matchArray[0] );
		this.name = matchArray[1];
		indexArray = jsonObj.controlArray;
	}
	else
	{
		matchArray = reXML.exec( binaryFile.binAsString );
		if (matchArray)
		{
			this.name = matchArray[1];
			indexArray = JSON.parse( matchArray[2] );
		}
		
	}


	if (!indexArray) this.valid = false;

	/**
	 * An object containing information about the properties of the effect, which can be used to generate expressions.<br />
	 * Access properties with their names, using the group hierarchy.<br />
	 * The info available for each property depends on the original pseudo effect, but there's at least the type and the index of the property.
	 * @example
     * var pseudoEffect = new DuAEPseudoEffect( pseudoEffectBin );
	 * var propIndex = pseudoEffect.props["groupName"]["PropertyName"].index;
	 * var effect = pseudoEffect.apply( aLayer );
	 * var expression = 'thisLayer.effect("' + pseudoEffect.name + '")(' + propIndex + ');';
	 * @type {Object}
	 * @name props
	 * @memberof DuAEPseudoEffect
	 * @readonly
	 */
	this.props = {};
	
	// Recursive method to get props
	var textOffset = 0;
	var mn = this.matchName;
	function getProps( groupIndex )
	{
		// The group
		var groupProps = {};

		// Each Text prop offsets the indices
		//textOffset = def(textOffset, 0);

		// For next props until we find the end of the group
		for(var i = groupIndex, n = indexArray.length; i < n; i++)
		{
			var prop = indexArray[i];

			// Recursion if it's a new group
			if ( prop.type == 'group' )
			{
				var newGroup = getProps( i+1 );
				// update properties for the group
				newGroup.index = i+1+textOffset;
				newGroup.name = prop.name;
				newGroup.invisible = prop.invisible;
				// Jump to the end
				i = newGroup.lastIndex;
				groupProps[prop.name] = newGroup;
			}
			// finished this group
			else if ( prop.type == 'endgroup' )
			{
				groupProps.lastIndex = i;
				return groupProps;
			}
			// It's a prop
			else
			{
				// We just need to set the index
				prop.index = i+1+textOffset;
				groupProps[prop.name] = prop;
				if(prop.type == 'text') textOffset++;
			}
		}

		return groupProps;
	}

	this.props = getProps( 0 );

	// Finally, extract the file

	/**
	 * The ffx file
	 * @type {File}
	 * @name file
	 * @memberof DuAEPseudoEffect
	 * @readonly
	 */
	this.file = binaryFile.toFile();

}

/**
	* This method adds the pseudo effect to a layer
	* @param {Layer} layer - The layer
	* @param {string} [name=this.name] - A name for the effect
	* @return {PropertyGroup|null} The effect. May be null if the pseudo effect was not parsed correctly
    */
DuAEPseudoEffect.prototype.apply = function(layer,name)
{
	name = def( name, this.name );

	return DuAELayer.addPseudoEffect( layer, this.file, this.matchName, name);
}
//*/
// ==================== |-----| ====================
// ==================== | api | ====================
// ==================== |-----| ====================

// The API which extends DuAEF & DuESF

// ==================== |-------------| ====================
// ==================== | api_version | ====================
// ==================== |-------------| ====================

/**
 * <h3>Duduf Groups</h3>
 * <p><i>Group After Effects layers by tags, and run batch methods on them.</i><br />
 * <code>DuGR</code> is the high-level interface to the layer groups,<br />
 * and it relies on {@link DuAETag}, a lower-level interface which adds tags on layers,<br />
 * and stores data in these tags.</p>
 * <p>A group is created using {@link DuGR.addSelectedLayersToGroup DuGR.addSelectedLayersToGroup()} or {@link DuAETag.addGroup DuAETag.addGroup()}.<br />
 * Groups can be renamed with {@link DuGR.renameGroup DuGR.renameGroup()},<br />
 * They can be removed with {@link DuGR.removeGroup DuGR.removeGroup()}.<br />
 * You can run a method on all layers belonging to a set of groups with {@link DuGR.do DuGR.do()}.</p>
 * <p>{@link DuGR.Group} contains predefined groups to sort layers by type or attribute and use {@link DuGR.do DuGR.do()} on them.</p>
 * <P>You can use {@link DuGR.isolate DuGR.isolate()} to isolate layers in the timeline, the comp panel, or both,<br />
 * use {@link DuGR.exitIsolation DuGR.exitIsolation()} to get out of isolation mode.</p>
 * <p>DuGR requires <i>DuAEF</i>, the <i>Duduf After Effects Framework</i>. Two builds of the <i>DuGR API</i> are available:<br />
 * <ul><li><code>DuGR_api.jsxinc</code> does not include <i>DuAEF</i>, and can be used to compine multiple <i>Duduf APIs</i> with a single copy of <i>DuAEF</i>.<br />
 * Be careful to grab the right version of <i>DuAEF</i> in this case.</li>
 * <li><code>DuAEF_DuGR_api.jsxinc</code> includes all dependencies, with <i>DuAEF</i>, and is easier to include in your scripts.</li></ul></p>
 * @example
 * // Encapsulate everything to avoid global variables!
 * // The parameter is either undefined (stand alone script) or the panel containing the ui (ScriptUI)
 * (function(thisObj)
 * {
 *      // If you only need DuGR, just include DuAEF_DuGR_api
 *      #include "DuAEF_DuGR_api.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);
 * @example
 * // Encapsulate everything to avoid global variables!
 * // The parameter is either undefined (stand alone script) or the panel containing the ui (ScriptUI)
 * (function(thisObj)
 * {
 *      // If you need to combine DuGR and other APIs like DuIO or DuIK
 *      // Include DuAEF first, and then stand-alone APIs
 *      #include "DuAEF.jsxinc";
 *      #include "DuGR_api.jsxinc";
 *      #include "DuIK_api.jsxinc";
 *      #include "DuIO_api.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 - 2022 Nicolas Dufresne, RxLaboratory
 * @version 5.0.1
 * @requires DuAEF>=1.0.0
 * @see {@link DuAETag} for low-level group management
 * @category DuGR
 * @license GPL-3.0 <br />
 * DuGR 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 />
 * DuGR 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 DuGR. If not, see {@link http://www.gnu.org/licenses/}.
 */
var DuGR = {}
DuGR.version = "5.0.1";
/**
 * Predefined and automatic groups
 * @enum {string}
 */
DuGR.Group = {
    /**
     * All layers in the composition
     */
    ALL: 'DuGR.all',
    /**
     * Selected layers
     */
    SELECTED: 'DuGR.selected',
    /**
     * Layers included in a custom group
     */
    GROUPED: 'DuGR.grouped',
    /**
     * Precompositions
     */
    COMP: 'DuGR.type.comp',
    /**
     * Null objects
     */
    NULL: 'DuGR.type.null',
    /**
     * Solids
     */
    SOLID: 'DuGR.type.solid',
    /**
     * Shape layers
     */
    SHAPE: 'DuGR.type.shape',
    /**
     * Text layers
     */
    TEXT: 'DuGR.type.text',
    /**
     * Adjustement layers
     */
    ADJUSTMENT: 'DuGR.type.adjustment',
    /**
     * 3D Light layers
     */
    LIGHT: 'DuGR.type.light',
    /**
     * Camera layers
     */
    CAMERA: 'DuGR.type.camera',
    /**
     * Visible layers
     */
    VISIBLE: 'DuGR.attr.visible',
    /**
     * Audio layers
     */
    SOUND: 'DuGR.attr.sound',
    /**
     * Solo layers
     */
    SOLO: 'DuGR.attr.solo',
    /**
     * Locked layers
     */
    LOCKED: 'DuGR.attr.locked',
    /**
     * Shy layers
     */
    SHY: 'DuGR.attr.shy',
    /**
     * Effects enabled
     */
    EFFECTS: 'DuGR.attr.effects',
    /**
     * Motion Blur enabled
     */
    MB: 'DuGR.attr.mb',
    /**
     * 3D layers
     */
    THREE_D: 'DuGR.attr.three_d',
    /**
     * Guide layers
     */
    GUIDE: 'DuGR.attr.guide',
    /**
     * The layer does not have any parent
     */
    ORPHAN: 'DuGR.hierarchy.orphan',
    /**
     * The layer has at least one child
     */
    HAS_CHILD: 'DuGR.hierarchy.hasChildren',
    /**
     * This is a special group: all layers in there will always be ignored by all methods.
     */
    IGNORED: '::Ignored::',
    /**
     * Layer styles are enabled.
     */
    LAYER_STYLES: 'DuGR.layerStyles',
    /**
     * Layer style: Drop shadow is enabled.
     */
    STYLE_DROP_SHADOW: 'DuGR.style.dropShadow',
    /**
     * Layer style: Inner shadow is enabled.
     */
    STYLE_INNER_SHADOW: 'DuGR.style.innerShadow',
    /**
     * Layer style: Outer glow is enabled..
     */
    STYLE_OUTER_GLOW: 'DuGR.style.outerGlow',
    /**
     * Layer style: Inner glow is enabled.
     */
    STYLE_INNER_GLOW: 'DuGR.style.innerGlow',
    /**
     * Layer style: Bevel is enabled.
     */
    STYLE_BEVEL: 'DuGR.style.bevel',
    /**
     * Layer style: Satin is enabled.
     */
    STYLE_SATIN: 'DuGR.style.satin',
    /**
     * Layer style: Color overlay is enabled.
     */
    STYLE_COLOR: 'DuGR.style.color',
    /**
     * Layer style: Gradient Overlay is enabled.
     */
    STYLE_GRADIENT: 'DuGR.style.gradient',
    /**
     * Layer style: Stroke is enabled.
     */
    STYLE_STROKE: 'DuGR.style.stroke',
    /**
     * Animation: at current time.
     */
    AT_TIME: 'DuGR.animation.atTime',
    /**
     * Animation: in the current time range.
     */
    IN_TIME_RANGE: 'DuGR.animation.inTimeRange',
    /**
     * Animation: has keyframes.
     */
    HAS_KEYFRAMES: 'DuGR.animation.hasKeyframes',
    /**
     * Animation: has expressions.
     */
    HAS_EXPRESSIONS: 'DuGR.animation.hasExpressions',
    /**
     * Matte: has at least one mask.
     */
    HAS_MASK: 'DuGR.matte.hasMask',
    /**
     * Matte: has track matte.
     */
    HAS_MATTE: 'DuGR.matte.hasMatte',
    /**
     * Matte: is used as a track matte.
     */
    IS_MATTE: 'DuGR.matte.isMatte',
    /**
     * Matte: Preserve transparency is enabled.
     */
    PRESERVE_TRANSPARENCY: 'DuGR.matte.preserveTransparency',
    /**
     * Matte: The blending mode is normal.
     */
    NORMAL_BLENDING: 'DuGR.matte.normalBlending',
    /**
     * Matte: The blending mode is not normal.
     */
    OTHER_BLENDING: 'DuGR.matte.otherBlending',
    /**
     * Attribute: has effects.
     */
    OTHER_BLENDING: 'DuGR.attr.hasEffects',
}

/**
 * The way the warning frame can be displayed
 * @enum {int}
 * @readonly
 */
DuGR.WarningFrameMode = {
    /**
     * No frame at all
     */
    NONE: 0,
    /**
     * Above all layers
     */
    ABOVE: 1,
    /**
     * Below all layers
     */
    BELOW: 2
}

/**
 * The way layers are isolated
 * @enum {int}
 * @readonly
 */
DuGR.IsolationMode = {
    /**
     * No isolation
     */
    NONE: 0,
    /**
     * Both the timeline and the comp panel
     */
    BOTH: 1,
    /**
     * Only in the timeline
     */
    TIMELINE: 2,
    /**
     * Only in the comp panel
     */
    COMP_PANEL: 3
}

/**
 * How to isolate layers in the comp panel
 * @enum {string}
 * @readonly
 */
DuGR.CompIsolationMode = {
    HIDE: 0,
    WIREFRAME: 1
}

/**
 * The name of the warning frame layer
 * @default "DuGR::Isolation"
 * @type {string}
 * @memberof DuScriptUI.String
 */
DuGR.DUGR_FRAME_NAME = "DuGR::Isolation";

/**
 * The key for DuGR isolation mode
 * @default 'dugrIsolationModed'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_ISOLATION_MODE = 'dugrIsolationMode';
/**
 * Is the layer isolated or not
 * @default 'dugrIsolated'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_HIDDEN = 'dugrIsolated';
/**
 * If the layer was shy before isolation
 * @default 'dugrWasShy'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_WAS_SHY = 'dugrWasShy';
/**
 * If the layer was enabled before isolation
 * @default 'dugrWasShy'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_WAS_ENABLED = 'dugrWasEnabled'
/**
 * If the layer was locked before isolation
 * @default 'dugrWasLocked'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_WAS_LOCKED = 'dugrWasLocked'
/**
 * The layer quality before isolation. The value may be one of 'best', 'draft', 'wireframe'.
 * @default 'dugrPreviousQuality'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_PREVIOUS_QUALITY = 'dugrPreviousQuality'
/**
 * If the layer had a tag before isolation.
 * @default 'dugrHadTag'
 * @readonly
 * @memberof DuAETag.Key
 */
DuAETag.Key.DUGR_HAD_TAG = 'dugrHadTag'

/**
 * Checks if a layer is included in the given groups
 * @param {Layer} layer The layer to test
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {MarkerValue} [tag] If you have the tag as returned by {@link DuAETag.get} or {@link DuAETag.set}, providing it here improves performance.
 * @param {Boolean} [and=false] If false, the layer must be contained in at least one of the groups, otherwise in all the groups.
 * @returns {Boolean} true if the layer belongs to at least one of the given groups
 */
DuGR.inGroups = function ( layer, groups, tag, and )
{
    and = def(and, false);

    // The Dugr layer is not in any group
    if (layer.name == i18n._(DuGR.DUGR_FRAME_NAME)) return false;

    var ok = false;
    for( var i = 0, n = groups.length; i < n; i++)
    {
        ok = false;

        var layerStyles = layer.property("ADBE Layer Styles");
        var comp = layer.containingComp;
        var workAreaEnd = comp.workAreaStart + comp.workAreaDuration;

        var group = groups[i];

        if (group == DuGR.Group.ALL) ok = true;
        else if (group == DuGR.Group.SELECTED ) ok = layer.selected;
        else if (group == DuGR.Group.GROUPED ) ok = DuAETag.getGroups( layer ).length > 0;
        else if (group == DuGR.Group.COMP ) ok = DuAELayer.isComp( layer );
        else if (group == DuGR.Group.NULL ) ok = DuAELayer.isNull( layer );
        else if (group == DuGR.Group.SOLID ) ok = DuAELayer.isSolid( layer );
        else if (group == DuGR.Group.SHAPE ) ok = layer instanceof ShapeLayer;
        else if (group == DuGR.Group.TEXT ) ok = layer instanceof TextLayer;
        else if (group == DuGR.Group.ADJUSTMENT ) ok = DuAELayer.isAdjustment( layer );
        else if (group == DuGR.Group.LIGHT ) ok =  layer instanceof LightLayer;
        else if (group == DuGR.Group.CAMERA ) ok = layer instanceof CameraLayer;
        else if (group == DuGR.Group.VISIBLE ) ok = layer.enabled;
        else if (group == DuGR.Group.SOUND ) ok = layer.hasAudio;
        else if (group == DuGR.Group.SOLO ) ok = layer.solo;
        else if (group == DuGR.Group.LOCKED ) ok = layer.locked;
        else if (group == DuGR.Group.SHY ) ok = layer.shy;
        else if (group == DuGR.Group.EFFECTS) ok = layer.effectsActive && layer.property("ADBE Effect Parade").numProperties > 0; 
        else if (group == DuGR.Group.MB ) ok = layer.motionBlur;
        else if (group == DuGR.Group.THREE_D ) ok = DuAELayer.isThreeD( layer );
        else if (group == DuGR.Group.GUIDE ) ok = layer.guideLayer;
        else if (group == DuGR.Group.ORPHAN ) ok = !layer.parent;
        else if (group == DuGR.Group.HAS_CHILD ) ok = DuAELayer.hasChild(layer);

        else if (group == DuGR.Group.LAYER_STYLES ) ok = layerStyles.enabled;
        else if (group == DuGR.Group.STYLE_DROP_SHADOW) ok = layerStyles.property("dropShadow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_INNER_SHADOW ) ok = layerStyles.property("innerShadow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_OUTER_GLOW ) ok = layerStyles.property("outerGlow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_INNER_GLOW ) ok = layerStyles.property("innerGlow/enabled").enabled;
        else if (group == DuGR.Group.STYLE_BEVEL ) ok = layerStyles.property("bevelEmboss/enabled").enabled;
        else if (group == DuGR.Group.STYLE_SATIN ) ok = layerStyles.property("chromeFX/enabled").enabled;
        else if (group == DuGR.Group.STYLE_COLOR ) ok = layerStyles.property("solidFill/enabled").enabled;
        else if (group == DuGR.Group.STYLE_GRADIENT ) ok = layerStyles.property("gradientFill/enabled").enabled;
        else if (group == DuGR.Group.STYLE_STROKE ) ok = layerStyles.property("frameFX/enabled").enabled;

        else if (group == DuGR.Group.AT_TIME ) ok = layer.inPoint <= comp.time && layer.outPoint >= comp.time;       
        else if (group == DuGR.Group.IN_TIME_RANGE ) ok = layer.inPoint < workAreaEnd && layer.outPoint > comp.workAreaStart;
        else if (group == DuGR.Group.HAS_KEYFRAMES ) ok = DuAELayer.hasKeys(layer);
        else if (group == DuGR.Group.HAS_EXPRESSIONS ) ok = DuAELayer.hasExpressions(layer);
        
        else if (group == DuGR.Group.HAS_MASK ) ok = DuAELayer.hasMask(layer);
        else if (group == DuGR.Group.HAS_MATTE ) ok = layer.hasTrackMatte;
        else if (group == DuGR.Group.IS_MATTE ) ok = layer.isTrackMatte;
        else if (group == DuGR.Group.PRESERVE_TRANSPARENCY ) ok = layer.preserveTransparency;
        else if (group == DuGR.Group.NORMAL_BLENDING ) ok = layer.blendingMode == BlendingMode.NORMAL;
        else if (group == DuGR.Group.OTHER_BLENDING ) ok = layer.blendingMode != BlendingMode.NORMAL;

        else
        {
            // Get the custom groups
            var layerGroups = DuAETag.getGroups( layer );
            if (layerGroups.length == 0) ok = false;
            layerGroups = new DuList( layerGroups );
            if (layerGroups.indexOf(group) >= 0) ok = true;
        }
        
        if (ok && !and) return true;
        if (!ok && and) return false;
    }

    return ok;
}

/**
 * Runs a function for each layer in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {DuGR~doCallBack} callback The function to run on each layer.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {DuGR~doNotCallBack} [notCallback] The function to run on each layer IF it is NOT in the groups.
 * @param {Boolean} [and=false] If false, runs the function on the layers contained in at least one of the groups, otherwise in all the groups.
 */
DuGR.do = function( groups, callback, invert, comp, notCallback, and )
{
    notCallback = def(notCallback, function(){} );
    comp = def(comp, DuAEProject.getActiveComp());
    invert = def(invert, false);

    if (!comp) return;
    if (comp.layers.length == 0) return;

    var returnValue;

    new DuList(comp.layers).do( function( layer ){
        if (DuGR.inGroups(layer, [DuGR.Group.IGNORED], undefined, and)) return;
        var ok = DuGR.inGroups(layer, groups, undefined, and);
        if (invert) ok = !ok;
        if (ok) returnValue = callback( layer, returnValue );
        else notCallback( layer );
    } );
}

/**
 * A function passed to {@link Dugr.do}.
 * @callback doCallBack
 * @memberof DuGR
 * @param {Layer} layer The layer on which the function operates.
 * @param {any} returnValue The return value of the previous iteration. Undefined during the first iteration.
 * @return {any} What the function returns will be automatically passed as a second argument on the next iteration.<br />
 * This is useful if the function has to set a toogle on the first layer, and then re-use it, for example.
 */

/**
 * Toggles the visibility of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleVisibility = function ( groups, invert, comp )
{
    function f( layer, enabled)
    {
        if (layer.isTrackMatte) return;
        enabled = def( enabled, !layer.enabled );
        layer.enabled = enabled;
        return enabled;
    }

    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the audio of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleSound = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        enabled = def( enabled, !layer.audioEnabled );
        layer.audioEnabled = enabled;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the solo mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleSolo = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        if (!layer.enabled) return enabled;
        enabled = def( enabled, !layer.solo );
        layer.solo = enabled;
        return enabled;
    }
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the lock of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleLocked = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        enabled = def( enabled, !layer.locked );
        layer.locked = enabled;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the shy mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.toggleShy = function( groups, invert, comp )
{
    function f( layer, enabled)
    {
        enabled = def( enabled, !layer.shy );
        layer.shy = enabled;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the collapse transformation/continuous rasterization mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleCollapseTransformation = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.collapseTransformation );
        layer.collapseTransformation = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the guide mode of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleGuide = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.guideLayer );
        layer.guideLayer = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Changes the render quality of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleQuality = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, quality)
    {
        if (layer.locked && !allowLockedChanges) return quality;

        var locked = layer.locked;
        layer.locked = false;

        if (typeof quality === 'undefined')
        {
            quality = layer.quality;
			if (quality == LayerQuality.BEST) quality = LayerQuality.DRAFT;
			else if (quality == LayerQuality.DRAFT) quality = LayerQuality.WIREFRAME;
			else if (quality == LayerQuality.WIREFRAME) quality = LayerQuality.BEST;
        }
        layer.quality = quality;

        layer.locked = locked;
        return quality;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the the effects of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleEffects = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.effectsActive );
        layer.effectsActive = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the effects of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleFrameBlending = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, frameBlending)
    {
        if (layer.locked && !allowLockedChanges) return frameBlending;
        if (!(layer instanceof AVLayer)) return frameBlending;
        if (!layer.source.hasVideo) return;


        var locked = layer.locked;
        layer.locked = false;

        if (typeof frameBlending === 'undefined')
        {
            frameBlending = layer.frameBlendingType;
            if (frameBlending == FrameBlendingType.PIXEL_MOTION) frameBlending = FrameBlendingType.FRAME_MIX;
            else if (frameBlending == FrameBlendingType.FRAME_MIX) frameBlending = FrameBlendingType.NO_FRAME_BLEND;
            else if (frameBlending == FrameBlendingType.NO_FRAME_BLEND) frameBlending = FrameBlendingType.PIXEL_MOTION;
        }

        layer.frameBlendingType = frameBlending;

        layer.locked = locked;
        return frameBlending;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the motion blur of all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleMotionBlur = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.motionBlur );
        layer.motionBlur = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the adjustment layer mode for all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleAdjustment = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.adjustmentLayer );
        layer.adjustmentLayer = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Toggles the 3D mode for all layers in the groups.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [allowLockedChanges=true] If set to false, locked layers won't be changed.
 */
DuGR.toggleThreeD = function( groups, invert, comp, allowLockedChanges )
{
    function f( layer, enabled)
    {
        if (layer.locked && !allowLockedChanges) return enabled;

        var locked = layer.locked;
        layer.locked = false;

        enabled = def( enabled, !layer.threeDLayer );
        layer.threeDLayer = enabled;

        layer.locked = locked;
        return enabled;
    }
    
    DuGR.do( groups, f, invert, comp);
}

/**
 * Selects the layers.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [and=false] If false, selects the layers contained in at least one of the groups, otherwise in all the groups.
 */
DuGR.select = function( groups, invert, comp, and )
{
    function f( layer )
    {
        if (layer.locked) return;
        layer.selected = true;
    }

    function nf( layer )
    {
        if (layer.locked) return;
        layer.selected = false;
    }
    
    DuGR.do( groups, f, invert, comp, nf, and);
}

// Low level undocumented method. Creates the warning frame.
DuGR.createDugrLayer = function( comp, frameMode, isolationMode )
{
    comp = def( comp, DuAEProject.getActiveComp() );
    if (!comp) return;

    //add rect
    sq = comp.layers.addShape();
    sq.shy = true;
    sq.guideLayer = true;
    sq.name = i18n._(DuGR.DUGR_FRAME_NAME);

    var tag = DuAETag.set( sq, i18n._(DuGR.DUGR_FRAME_NAME), undefined );
    DuAETag.setValue(sq, DuAETag.Key.DUGR_ISOLATION_MODE, isolationMode, tag);

    if (frameMode != DuGR.WarningFrameMode.ABOVE) sq.moveToEnd();

    if (frameMode == DuGR.WarningFrameMode.NONE)
    {
        sq.locked = true;
        return sq;
    }

    var gr = sq("ADBE Root Vectors Group").addProperty("ADBE Vector Group");
    var groupContent = gr.property("ADBE Vectors Group");
    var shapeProp = groupContent.addProperty("ADBE Vector Shape - Rect");
    shapeProp("ADBE Vector Rect Size").setValue([comp.width,comp.height]);
    var stroke = groupContent.addProperty("ADBE Vector Graphic - Stroke");
    stroke("ADBE Vector Stroke Color").setValue([0.8,0,0]);
    stroke("ADBE Vector Stroke Opacity").setValue(50);
    stroke("ADBE Vector Stroke Width").setValue(comp.width/100);

    sq.locked = true;

    return sq;
}

// Low level undocumented method. Removes the warning frame.
DuGR.removeDugrLayer = function( comp )
{
    var layer = DuGR.getDugrLayer( comp );
    if (!layer) return;
    layer.locked = false;
    layer.remove();
}

// Low level undocumented method. Gets the warning frame.
DuGR.getDugrLayer = function( comp )
{
    comp = def( comp, DuAEProject.getActiveComp() );
    if (!comp) return;
    if (comp.numLayers == 0) return;

    //To be faster, check first and last layer first
    if (comp.layer(1).name == i18n._(DuGR.DUGR_FRAME_NAME)) return comp.layer(1);

    if (comp.layer(comp.numLayers).name == i18n._(DuGR.DUGR_FRAME_NAME))  return comp.layer(comp.numLayers);

    for (var i = 1, n = comp.numLayers; i <= n; i++)
    {
        if (comp.layer(i).name == i18n._(DuGR.DUGR_FRAME_NAME)) return comp.layer(i);
    }

    return null;
}

/**
 * Checks the isolation mode of the comp.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to check.
 * @return {DuGR.IsolationMode} The isolation mode.
 */
DuGR.isolationMode = function( comp )
{
    comp = def(comp, DuAEProject.getActiveComp() );
    if (!comp) return DuGR.IsolationMode.NONE;
    if (comp.numLayers == 0) return DuGR.IsolationMode.NONE;

    var dugrLayer = DuGR.getDugrLayer( comp );
    if (!dugrLayer) return DuGR.IsolationMode.NONE;

    var mode = DuAETag.getValue( dugrLayer, DuAETag.Key.DUGR_ISOLATION_MODE, DuAETag.Type.INT ) ;
    if (mode == null) mode = DuGR.IsolationMode.NONE;
    return mode;
}

/**
 * Activates the isolation mode.
 * @param {string[]} groups The list of group names. It can include custom groups, or predefined names taken from {@link DuGR.Group}.
 * @param {Boolean} [invert=false] When true, the list of groups is a blacklist and the layer must not be contained in any one of them.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to isolate.
 * @param {DuGR.WarningFrameMode} [frameMode=DuGR.WarningFrameMode.BELOW] How the warning frame must be shown
 * @param {DuGR.IsolationMode} [isolationMode=DuGR.IsolationMode.BOTH] What needs to be hidden.
 * @param {DuGR.CompIsolationMode} [compIsolationMode=DuGR.CompIsolationMode.HIDE] How to hide the layers
 * @param {Boolean} [lockHiddenLayers=true] whether to lock the layers being hidden.
 */
DuGR.isolate = function( groups, invert, comp, frameMode, isolationMode, compIsolationMode, lockHiddenLayers )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    frameMode = def(frameMode, DuGR.WarningFrameMode.BELOW);
    isolationMode = def(isolationMode, DuGR.IsolationMode.BOTH);
    compIsolationMode = def(compIsolationMode, DuGR.CompIsolationMode.HIDE);
    lockHiddenLayers = def(lockHiddenLayers, true);
    invert = def(invert, false);

    if (isolationMode == DuGR.IsolationMode.NONE) DuGR.exitIsolation( comp );

    var timeline = isolationMode == DuGR.IsolationMode.BOTH || isolationMode == DuGR.IsolationMode.TIMELINE;
    var compPanel = isolationMode == DuGR.IsolationMode.BOTH || isolationMode == DuGR.IsolationMode.COMP_PANEL;

    // The previous mode
    var previousMode = DuGR.isolationMode( comp );

    // AE deselects the layers on creation of the new shape... Store the list
    var selectedLayers = DuAEComp.unselectLayers( comp );

    // Create the frame
    var dugrFrameLayer;
    if (previousMode == DuGR.IsolationMode.NONE) dugrFrameLayer = DuGR.createDugrLayer( comp, frameMode, isolationMode );
    else
    {
        dugrFrameLayer = DuGR.getDugrLayer(comp);
        DuAETag.setValue(sq, DuAETag.Key.DUGR_ISOLATION_MODE, isolationMode);
    }

    // Restore the selection
    DuAEComp.selectLayers(selectedLayers);

    // Keep the shy status of the comp
    if (previousMode == DuGR.IsolationMode.NONE || previousMode == DuGR.IsolationMode.COMP_PANEL)
    {
        DuAETag.setValue( dugrFrameLayer, DuAETag.Key.DUGR_COMP_SHY, comp.hideShyLayers);
    }

    // Activate shy mode
    if (timeline) comp.hideShyLayers = true;

    // Loop layers to hide/shy them or restore their previous state.
    for (var i = 1, n = comp.numLayers; i <= n; i++)
    {
        var layer = comp.layer(i);

        // Get/Set the dugr tag
        var tag = DuAETag.set( layer, undefined, undefined );

        // whether to isolate the layer
        var hide = !DuGR.inGroups( layer, groups, tag );
        if (invert) hide = !hide;

        // whether the layer was already isolated
        var wasHidden = DuAETag.getValue( layer, DuAETag.Key.DUGR_HIDDEN, DuAETag.Type.BOOL, tag );

        // Nothing to do
        if (!wasHidden && !hide) continue;

        if (!wasHidden)
        {
            DuGR.saveStatus( layer, tag );
        }
        
        if (hide)
        {
            DuGR.hideLayer( layer, timeline, compPanel, compIsolationMode, lockHiddenLayers, tag );
        }
        else 
        {
            DuGR.unHideLayer( layer, tag );
        }
    }
}

// low-level undocumented method to save the status of a layer before hiding it.
DuGR.saveStatus = function( layer, tag )
{
    var tagParams = tag.getParameters();

    tagParams[DuAETag.Key.DUGR_HIDDEN] = true;
    tagParams[DuAETag.Key.DUGR_WAS_SHY] = layer.shy;
    tagParams[DuAETag.Key.DUGR_WAS_ENABLED] = layer.enabled;
    tagParams[DuAETag.Key.DUGR_WAS_LOCKED] = layer.locked;
    //params[DuAETag.Key.DUGR_HAD_TAG] = hadTag;

    var quality = 'best';
    if (layer.quality == LayerQuality.DRAFT) quality = 'draft';
    else if (layer.quality == LayerQuality.WIREFRAME) quality = 'wireframe';
    tagParams[DuAETag.Key.DUGR_PREVIOUS_QUALITY] = quality;

    tag.setParameters( tagParams );

    DuAETag.set( layer, undefined, tag );
}

// low-level undocumented method to put a layer to isolation
DuGR.hideLayer = function( layer, timeline, compPanel, compIsolationMode, lockHiddenLayers, tag )
{
    // Ignore the Dugr Layer
    if (layer.name == i18n._(DuGR.DUGR_FRAME_NAME)) return;

    if (DuGR.inGroups(layer, [DuGR.Group.IGNORED])) return;
    // Hide
    if (timeline) layer.shy = true;
    else // Restore shy status
    {
        var wasShy = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_SHY, DuAETag.Type.BOOL, tag );
        if (wasShy) layer.shy = true;
        else if (wasShy != null) layer.shy = false;
    }
    if (compPanel)
    {
        if (compIsolationMode == DuGR.CompIsolationMode.WIREFRAME) layer.quality = LayerQuality.WIREFRAME;
        else layer.enabled = false;
    }
    else  // Restore visibility status
    {
        var wasEnabled = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_ENABLED, DuAETag.Type.BOOL, tag );
        if (wasEnabled) layer.enabled = true;
        else if (wasEnabled != null)  layer.enabled = false;

        var quality = DuAETag.getValue( layer, DuAETag.Key.DUGR_PREVIOUS_QUALITY, DuAETag.Type.STRING, tag );
        if (quality == 'best') layer.quality = LayerQuality.BEST;
        else if (quality == 'draft') layer.quality = LayerQuality.DRAFT;
        else if (quality == 'wireframe') layer.quality = LayerQuality.WIREFRAME;
    }
    if (lockHiddenLayers) layer.locked = true;

    DuAETag.setValue( layer, DuAETag.Key.DUGR_HIDDEN, true, tag );
}

// low-level undocumented method to remove a layer from isolation
DuGR.unHideLayer = function ( layer, tag )
{
    if (DuGR.inGroups(layer, [DuGR.Group.IGNORED])) return;

    var wasShy = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_SHY, DuAETag.Type.BOOL, tag );
    if (wasShy) layer.shy = true;
    else if (wasShy != null) layer.shy = false;

    var wasEnabled = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_ENABLED, DuAETag.Type.BOOL, tag );
    if (wasEnabled) layer.enabled = true;
    else if (wasEnabled != null)  layer.enabled = false;

    var wasLocked = DuAETag.getValue( layer, DuAETag.Key.DUGR_WAS_LOCKED, DuAETag.Type.BOOL, tag );
    if (wasLocked) layer.locked = true;
    else if (wasLocked != null) layer.locked = false;

    var quality = DuAETag.getValue( layer, DuAETag.Key.DUGR_PREVIOUS_QUALITY, DuAETag.Type.STRING, tag );
    if (quality == 'best') layer.quality = LayerQuality.BEST;
    else if (quality == 'draft') layer.quality = LayerQuality.DRAFT;
    else if (quality == 'wireframe') layer.quality = LayerQuality.WIREFRAME;

    DuAETag.setValue( layer, DuAETag.Key.DUGR_HIDDEN, false, tag );

    /*var hadTag = DuAETag.getValue( layer, DuAETag.Key.DUGR_HAD_TAG, DuAETag.Type.BOOL, tag );
    if (hadTag) DuAETag.setValue( layer, DuAETag.Key.DUGR_HIDDEN, false );
    else DuAETag.remove( layer );*/
}

/**
 * Exits the isolation mode.
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.exitIsolation = function( comp )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    // Reset shy mode
    var dugrFrameLayer = DuGR.getDugrLayer( comp )
    var wasShy = DuAETag.getValue( dugrFrameLayer, DuAETag.Key.DUGR_COMP_SHY, DuAETag.Type.BOOL);
    if (wasShy) comp.hideShyLayers = true;
    else comp.hideShyLayers = false;

    // Remove frame
    DuGR.removeDugrLayer( comp );  

    // Restore layers
    for (var i = 1, n = comp.numLayers; i <= n; i++)
    {
        var layer = comp.layer(i);
        var tag = DuAETag.get( layer );
        // Only hidden layers to improve performance
        var wasHidden = DuAETag.getValue( layer, DuAETag.Key.DUGR_HIDDEN, DuAETag.Type.BOOL, tag );
        if (wasHidden) DuGR.unHideLayer( layer, tag );
    }   
}

/**
 * Adds the selected layers to a (new) group
 * @param {string} group The group name
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 */
DuGR.addSelectedLayersToGroup = function( group, comp )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    var layers = comp.selectedLayers;
    for(var i = 0, n = layers.length; i < n; i++)
    {
        var layer = layers[i];
        DuAETag.addGroup( layer, group );
    }
}

/**
 * Gets the list of all groups
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to read.
 * @param {Boolean} [selectedLayers=false] When true, only from selected layers.
 * @returns {DuList} The list of groups
 */
DuGR.listGroups = function ( comp, selectedLayers )
{
    comp = def(comp, DuAEProject.getActiveComp());
    selectedLayers = def(selectedLayers, false);

    if (!comp) return new DuList();
    if (comp.numLayers == 0) return new DuList();
    if (selectedLayers && comp.selectedLayers.length == 0) return new DuList();

    var layers;
    if (selectedLayers) layers = comp.selectedLayers;
    else layers = comp.layers;
    layers = new DuList(layers);

    var groups = new DuList();
    layers.do( function(layer )
    {
        var g = DuAETag.getGroups( layer );
        groups.mergeUnique( g );
    } );
    groups.sort();
    return groups;
}

/**
 * Renames an existing group (in the comp only)
 * @param {string} previousName The current name
 * @param {string} newName The new name
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to read.
 */
DuGR.renameGroup = function( previousName, newName, comp )
{
    comp = def(comp, DuAEProject.getActiveComp());
    if (!comp) return;
    if (comp.numLayers == 0) return;

    for (var i = 1, n = comp.numLayers; i < n; i++)
    {
        DuAETag.renameGroup( comp.layer(i), previousName, newName );
    }
}

/**
 * Removes a group from the composition
 * @param {string} group The group to remove
 * @param {CompItem} [comp=DuAEProject.getActiveComp] The composition to update.
 * @param {Boolean} [selectedLayers=false] When true, only from selected layers.
 */
DuGR.removeGroup = function( group, comp, selectedLayers )
{
    comp = def(comp, DuAEProject.getActiveComp());
    selectedLayers = def(selectedLayers, false);

    if (!comp) return;
    if (comp.numLayers == 0) return;
    if (selectedLayers && comp.selectedLayers.length == 0) return;

    var layers;
    if (selectedLayers) layers = comp.selectedLayers;
    else layers = comp.layers;
    layers = new DuList(layers);

    layers.do( function(layer )
    {
        DuAETag.removeGroup( layer, group );
    } );
}